网络摄像头Androi端显示(mjpeg)源码分析

来源:互联网 发布:淘宝包裹卡片 编辑:程序博客网 时间:2024/06/10 17:48

转自:http://www.cnblogs.com/yihujiu/p/5997859.html


main.xml

复制代码
复制代码
<?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"    android:orientation="vertical" >          <TextView           android:id="@+id/Tips"          android:layout_width="fill_parent"          android:layout_height="wrap_content"          android:text="@string/init_tips"          android:textSize="40px"          android:textColor="#00ff00"          />       <Button         android:id="@+id/btn_network"         android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/login"        android:textSize="40px"        android:textColor="#00ff00"        />       <TextView        android:id="@+id/statc001"         android:layout_width="wrap_content"        android:layout_height="wrap_content"              android:textSize="40px"        android:textColor="#00ff00"        />       <Button            android:id="@+id/btn_video"           android:layout_width="wrap_content"           android:layout_height="wrap_content"           android:text="@string/move"           android:textSize="40px"           />       </LinearLayout>
复制代码

 

复制代码

 

flash.xml

复制代码
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"     android:gravity="center"    android:orientation="vertical" >           <TextView        android:id="@+id/hintTv"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/login_hint" />          <EditText            android:id="@+id/ip"            android:hint="@string/ip_hint"            android:layout_width="fill_parent"            android:layout_height="wrap_content"            android:gravity="center"            android:text="192.168.0.10"          />        <EditText            android:id="@+id/port"            android:layout_width="fill_parent"            android:layout_height="wrap_content"            android:text="8080"            android:gravity="center"                      />                    <Button        android:id="@+id/connect"        android:layout_width="fill_parent"        android:layout_height="40.0dip"        android:layout_marginLeft="10.0dip"        android:layout_marginRight="10.0dip"        android:layout_marginTop="20.0dip"        android:text="@string/connect"        android:textColor="#ffffffff"        android:textSize="18.0sp" />                          </LinearLayout>
复制代码

mainactivity.xml

复制代码
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="fill_parent"    android:layout_height="fill_parent" >        <TextView         android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:text="视频显示"/>      <com.mjpeg.view.MjpegView        android:id="@+id/mjpegview"        android:layout_width="fill_parent"        android:layout_height="fill_parent"         />    </RelativeLayout>
复制代码

strings.xml

复制代码
<?xml version="1.0" encoding="utf-8"?><resources>    <string name="login">连接服务器</string>    <string name="app_name">梧州学院图书馆刷卡入座系统</string>    <string name="move">视频</string>     <string name="init_tips">提示:请先打开WiFi或GPRS再连接网络</string>     <string name="people1">空座</string>     <string name="people2">有人</string>        <string name="login_hint">connecting......;</string>     <string name="ip">IP:</string>     <string name="ip_hint">请输入IP地址</string>     <string name="port">Port:</string>     <string name="port_hint">端口1000到65535</string>     <string name="connect">链接</string>     <string name="connect_failed">链接失败</string>    </resources>
复制代码

 

AndroidManifest.xml

复制代码
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="my.work.Library"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk android:minSdkVersion="15" />    <application        android:icon="@drawable/ic_launcher"        android:label="@string/app_name" >        <activity            android:name=".WsnActivty"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>                <activity            android:name=".FlashActivity"            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"            android:screenOrientation="portrait" >            <intent-filter>                                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>                  <activity            android:name=".LinearLayout_activity"            android:theme="@android:style/Theme.NoTitleBar.Fullscreen"            android:screenOrientation="portrait" >            <intent-filter>                                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>            </application>       <uses-permission android:name="android.permission.INTERNET"/>    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/></manifest>
复制代码

 

Generic.java

复制代码
package tools;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.FileReader;import java.io.IOException;import java.io.OutputStreamWriter;import java.net.InetAddress;import java.net.NetworkInterface;import java.net.SocketException;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;import java.util.Comparator;import java.util.Date;import java.util.Enumeration;import java.util.List;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.media.ThumbnailUtils;import android.os.Environment;import android.text.format.Time;import android.util.Log;import android.widget.Toast;public class Generic {    public static void showMsg(Context c, String msg, boolean flag){        if(flag)            /**             * Toast是已经用于显示给用户的控件,显示一段时间后消失,可以多久消失             * LENGTH_SHORT:断的显示时间             * LENGTH_LONG :长的显示时间             */            Toast.makeText(c, msg, Toast.LENGTH_SHORT).show();         else            Toast.makeText(c, msg, Toast.LENGTH_LONG).show();    }         // get sysTime    public static String getSysNowTime() {        Time localTime = new Time();        localTime.setToNow();        String strTime = localTime.format("%Y-%m-%d-%H-%M-%S");        return strTime;    }        /**     * 得到sdcard的路径     * @return 失败返回null     */    public static File getSdCardFile(){        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){            return Environment.getExternalStorageDirectory();        }        return null;    }            /**     * 获取所有连接到本wifi热点的手机IP地址     */      public static ArrayList<String> getConnectedIP() {          ArrayList<String> connectedIP = new ArrayList<String>();          try {              BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp"));              String line;             br.readLine();            while ((line = br.readLine()) != null) {                  String[] splitted = line.split(" ");                  if (splitted != null && splitted.length >= 4) {                      String ip = splitted[0];                      connectedIP.add(ip);                  }              }          } catch (Exception e) {              e.printStackTrace();          }                    return connectedIP;      }     /**     * 得到照片的缩略图     * @param f 照片文件     * @param w 图片缩小的目标宽度     * @param h 图片缩小的目标高度     * @return     * 1.根据android提供的BitmapFactory.Options类创建并设置好options     * 2.根据File获得流对象     * 3.根据BitmapFactory.decodeStream获得位图     * 4.改变图片为居中缩放,返回位图     */    public static Bitmap getShrinkedPic(File f){        Bitmap smallBitmap = null;                // 直接通过图片路径将图片转化为bitmap,并将bitmap压缩,避免内存溢出        BitmapFactory.Options options = new BitmapFactory.Options();        options.inSampleSize = 10;// 图片宽高都为原来的十分之一        options.inPreferredConfig = Bitmap.Config.ARGB_4444;// 每个像素占用2byte内存        options.inPurgeable = true;// 如果 inPurgeable        // 设为True的话表示使用BitmapFactory创建的Bitmap        // 用于存储Pixel的内存空间在系统内存不足时可以被回收        options.inInputShareable = true;        FileInputStream fInputStream;        try {            fInputStream = new FileInputStream(f);            // 建议使用BitmapFactory.decodeStream            Bitmap bitmap = BitmapFactory.decodeStream(                    fInputStream, null, options);// 直接根据图片路径转化为bitmap            smallBitmap = ThumbnailUtils.extractThumbnail(                    bitmap, 64, 48);// 创建所需尺寸居中缩放的位图        } catch (FileNotFoundException e) {            e.printStackTrace();            return null;        }                return smallBitmap;    }    /**     * Integer值越大,则排在前面     * @author Administrator     *     */    public static class DescendSortByIndex implements Comparator<Integer>{        /**         * @return 负数:object2<object1,正数:object2>object1,0:相等         */        @Override        public int compare(Integer object1, Integer object2) {                        return object2.compareTo(object1);        }            }        /**     * File的最后修改时间值越大,则排在前面     * @author Administrator     *     */    public static class DescendSortByTime implements Comparator<File>{        /**         * @return 负数:object2<object1,正数:object2>object1,0:相等         */        @Override        public int compare(File object1, File object2) {                        return (int) (object2.lastModified() - object1.lastModified());        }            }}
复制代码

Mjpeg.java

复制代码
package com.mjpeg.view;import java.io.IOException;import tools.Generic;import my.work.Library.R;import com.mjpeg.io.MjpegInputStream;import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.Rect;import android.graphics.Typeface;import android.util.AttributeSet;import android.util.Log;import android.view.SurfaceHolder;import android.view.SurfaceView;/** * 此类继承了SurfaceView实现了SurfaceHolder.Callback接口 * SurfaceView是视图类(view)的继承类,这个视图里内嵌入了一个专门用于绘制的Surface    ,可以控制这个Surface的格式和尺寸 * SurfaceView控制这个Surface的绘制位置 * surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。surfaceview提供了一个可见区域 * 只有在这个可见区域内 的surface部分内容才可见,可见区域外的部分不可见。surface的排版显示受到视图层级关系的影响 * 它的兄弟视图结点会在顶端显示,这意味者 surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件) * 可以通过SurfaceHolder接口访问这个surface,getHolder()方法可以得到这个接口 * surfaceview变得可见时    ,surface被创建;surfaceview隐藏前,surface被销毁;这样能节省资源。如果你要查看 surface被创建和销毁的时机 * 可以重载surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder) * surfaceview的核心在于提供了两个线程:UI线程和渲染线程,这里应注意: * 1> 所有SurfaceView和SurfaceHolder.Callback的方法都应该在UI线程里调用,一般来说就是应用程序主线程,渲染线程所要访问的各种变量应该作同步处理。 * 2> 由于surface可能被销毁,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之间有效, * 所以要确保渲染线程访问的是合法有效的surface * 整个过程:继承SurfaceView并实现SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()获得SurfaceHolder对象(Surface控制器)  * ---->SurfaceHolder.addCallback(callback)添加回调函数---->SurfaceHolder.lockCanvas()获得Canvas对象并锁定画布 * ----> Canvas绘画 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)结束锁定画图,并提交改变,将图形显示。 */public class MjpegView extends SurfaceView implements SurfaceHolder.Callback {    /*fps显示位置*/    public final static int POSITION_UPPER_LEFT = 9;    public final static int POSITION_UPPER_RIGHT = 3;    public final static int POSITION_LOWER_LEFT = 12;    public final static int POSITION_LOWER_RIGHT = 6;    /*图像显示模式*/    public final static int STANDARD_MODE = 1;//标准尺寸    public final static int KEEP_SCALE_MODE = 4;//保持宽高比例    public final static int FULLSCREEN_MODE = 8;//全屏    private Context mContext = null;    private MjpegViewThread mvThread = null;    private MjpegInputStream mIs = null;    private Paint overlayPaint = null;//用于fps涂层绘画笔    private boolean bIsShowFps = true;    private boolean bRun = false;    private boolean bsurfaceIsCreate = false;    private int overlayTextColor;    private int overlayBackgroundColor;    private int ovlPos;    private int dispWidth;//MjpegView的宽度    private int dispHeight;//MjpegView的高度    private int displayMode;//覆盖模式    public MjpegView(Context context) {        super(context);        init(context);    }        /**     * 因为在res/layout目录下的main.xml中作为自定义的控件使用了这个类,所以需要给此类提供带有属性形参的构造函数     * 当在MainActivity通过ID找到这自定义的控件时,该构造函数将被调用,所以将该构造函数设为public         * @param context     * @param attrs     */    public MjpegView(Context context, AttributeSet attrs) {        super(context, attrs);        init(context);    }    /**     * 类的私有方法     * 1.获得Surface控制器,为Surface控制器添加回调接口     * 2.新建渲染线程MjpegViewThread     * 3.新建覆盖画笔,设置文本的对齐方式、文本长度、字体、画笔文本颜色、画笔背景     * 4.设置覆盖动态文本的覆盖位置 //如果你只需要实现监控画面的功能,3和4步可以省略     * 5.设置MjpegView显示模式     * @param context     */    private void init(Context context) {        mContext = context;        SurfaceHolder holder = getHolder();        holder.addCallback(this);        mvThread = new MjpegViewThread(holder, context);        setFocusable(true);        overlayPaint = new Paint();        overlayPaint.setTextAlign(Paint.Align.LEFT);        overlayPaint.setTextSize(12);        overlayPaint.setTypeface(Typeface.DEFAULT);        overlayTextColor = Color.RED;        overlayBackgroundColor = Color.TRANSPARENT;        ovlPos = MjpegView.POSITION_UPPER_RIGHT;        displayMode = MjpegView.KEEP_SCALE_MODE;            }    /**     *  Surface的任何结构性结构性的改变(如格式,大小)将激发此方法     *  主要调用渲染线程的setSurfaceSize来设置Surface的宽和高     */    public void surfaceChanged(SurfaceHolder holder, int f, int w, int h) {        mvThread.setSurfaceSize(w, h);    }    /**     * Surface被销毁之前将激发此方法,这里只设置标记位,表示Surface“被销毁了”     */    public void surfaceDestroyed(SurfaceHolder holder) {        bsurfaceIsCreate = false;    }    /**     * Surface被第一次创建后将激发此方法,这里只设置标记位,表示Surface“被创建了”     */    public void surfaceCreated(SurfaceHolder holder) {        bsurfaceIsCreate = true;    }    /**     * setFps,getFps,set source都在MaiActivity使用     * @param b     */    public void setFps(boolean b) {        bIsShowFps = b;    }        public boolean getFps(){        return bIsShowFps;    }    public void setSource(MjpegInputStream source) {        mIs = source;    }        /**     * 开始播放线程     * 设置标记,表示“Surface被创建了”,然后调用渲染线程的的run方法启动渲染     */    public void startPlay() {        if (mIs != null) {            bRun = true;            mvThread.start();        }    }    /**     * 停止播放线程     * 1.先设置标记,表示"停止播放"     * 2.等待播放线程的退出     * 3.关闭输入流     */    public void stopPlay() {        bRun = false;        boolean retry = true;        while (retry) {            try {                mvThread.join();                retry = false;            } catch (InterruptedException e) {            }        }                //线程停止后关闭Mjpeg流(很重要)        mIs.closeInstance();    }    /**     * mjpegview的获取位图方法,调用渲染线程的获取位图方法     * @return     */    public Bitmap getBitmap(){        return mvThread.getBitmap();    }        /**     * 设置显示模式,在MainActivity的initview调用     * @param s     */    public void setDisplayMode(int s) {        displayMode = s;    }    /**     * 既然有设置显示模式,就应该也有获得显示模式,这是java在设置方法方面的风格     * @return     */    public int getDisplayMode() {        return displayMode;    }    /**     * 此渲染线程类在主类上是重点,应该重点掌握     * @author Administrator     *     */    public class MjpegViewThread extends Thread {        private SurfaceHolder mSurfaceHolder = null;        private int frameCounter = 0;        private long start = 0;        private Canvas c = null;        private Bitmap overlayBitmap = null;        private Bitmap mjpegBitmap = null;        private PorterDuffXfermode mode = null;        /**         * 用一个变量来保存传进来的surfaceHolder         * 新建一个目的图层和覆盖图层的相交模式,mjpegview为目的图层,覆盖图层为右上角的动态"文本"         * mode在calculateFps方法里使用         * @param surfaceHolder:Surfaceview控制器         * @param context : 上下文环境         */        public MjpegViewThread(SurfaceHolder surfaceHolder, Context context) {            mSurfaceHolder = surfaceHolder;            mode = new PorterDuffXfermode(PorterDuff.Mode.DST_OVER);/*相交时动态文本覆盖mjpegview*/        }                public Bitmap getBitmap(){            return mjpegBitmap;        }        /**         * 计算图像尺寸         * @param bmw bitmap宽         * @param bmh bitmap高         * @return 图像矩阵         */        private Rect destRect(int bmw, int bmh) {            int tempx;            int tempy;            /**             * 显示模式只会在全屏和半屏模式之间切换,根本不会进入STANDARD_MODE模式,故下面的if分支可以去掉             */            if (displayMode == MjpegView.STANDARD_MODE) {                tempx = (dispWidth / 2) - (bmw / 2);                tempy = (dispHeight / 2) - (bmh / 2);                return new Rect(tempx, tempy, bmw + tempx, bmh + tempy);            }            /**             * 一开始,程序处于KEEP_SCALE_MODE模式,表示半屏显示画面             */            if (displayMode == MjpegView.KEEP_SCALE_MODE) {                float bmasp = (float) bmw / (float) bmh;                bmw = dispWidth;                bmh = (int) (dispWidth / bmasp);/*宽是手机屏幕的一半*/                if (bmh > dispHeight) {                    bmh = dispHeight;                    bmw = (int) (dispHeight * bmasp);                }                tempx = (dispWidth / 2) - (bmw / 2);                tempy = (dispHeight / 2) - (bmh / 2);                /**                 * Rect(左边,顶边,右边,下边),功能是绘制一个特定坐标的矩形                 * 简单说就是左上角坐标为(0,0),右下角坐标为(bmw,bmh)                 */                return new Rect(0, 0, bmw + 0, bmh + 0);            }            /**             * 如果显示模式为全屏,则全屏显示画面             * dispWidth和dispHeight在下面的setSurfaceSize方法使用,它们表示mjpegview的宽和高             */            if (displayMode == MjpegView.FULLSCREEN_MODE)                return new Rect(0, 0, dispWidth, dispHeight);            return null;        }        /**         * 当mjpegview发生任何结构性的改变时,将激发此方法,前面也提到,渲染线程使用的各种变量需做同步处理         * synchronized内的就是同步代码块,为了防止线程之间对临界资源的竞争         * @param width         * @param height         */        public void setSurfaceSize(int width, int height) {            synchronized (mSurfaceHolder) {                dispWidth = width;                dispHeight = height;            }        }        /**         * 此方法被calculateFps使用,calculateFps又被渲染线程的run方法使用         * 功能是返回一个位图         * @param p:覆盖"文本"用的画笔         * @param text:要绘制的字符 如:帧         * @return bm         */        private Bitmap makeFpsOverlay(Paint p, String text) {            int nWidth, nHeight;                        Rect b = new Rect();            //int  a = b.left ;            /**             * 功能是获得从原点开始,字符围绕的最小的矩形             * text:字符             * 0:表示第一个字符             * text.length:测量的最后一个字符             * b:用于存放获得的字符矩形             * 获得了text的边界后就可以得到矩形的宽和高             */            p.getTextBounds(text, 0, text.length(), b);            nWidth = b.width() + 2;            nHeight = b.height() + 2;            /**             * 每一个像素4字节,根据上面获得的宽和高返回一个位图             */            Bitmap bm = Bitmap.createBitmap(nWidth, nHeight,                    Bitmap.Config.ARGB_8888);            /**             * Canvas :画布,这是图像处理的基本单元             * 画图时,需要4个重要的元素:             * 1.操作像素的位图             * 2.绘图到位图的画布              * 3.矩形              * 4. 描述颜色和绘制风格的画笔              * Canvas(bm):构造出一个要绘制到位图的画布             */            Canvas c = new Canvas(bm);        /**         * Paint类介绍           * Paint即画笔,在绘图过程中起到了极其重要的作用,画笔主要保存了颜色,           * 样式等绘制信息,指定了如何绘制文本和图形,画笔对象有很多设置方法,           * 大体上可以分为两类,一类与图形绘制相关,一类与文本绘制相关。                  *            * 1.图形绘制                 * setColor(int color);           * 设置绘制的颜色,使用颜色值来表示,该颜色值包括透明度和RGB颜色。             * setDither(boolean dither);                * setXfermode(Xfermode xfermode);           * 设置图形重叠时的处理方式,如合并,取交集或并集,经常用来制作橡皮的擦除效果           *            * 2.文本绘制           * setFakeBoldText(boolean fakeBoldText);           * 模拟实现粗体文字,设置在小字体上效果会非常差              * setSubpixelText(boolean subpixelText);           * 设置该项为true,将有助于文本在LCD屏幕上的显示效果           *            * setTextAlign(Paint.Align align);           * 设置绘制文字的对齐方向             * setTextSize(float textSize);           * 设置绘制文字的字号大小           * setTypeface(Typeface typeface);           * 设置Typeface对象,即字体风格,包括粗体,斜体以及衬线体,非衬线体等             */                       p.setColor(overlayBackgroundColor);// 背景颜色            c.drawRect(0, 0, nWidth, nHeight, p);/*绘制矩形*/            p.setColor(overlayTextColor);// 文字颜色            /**             * 画布的绘制文字方法             * test:要绘制的字符             * -b.left:字符起始位置的x坐标,这里是矩形的左边             * (nHeight / 2) - ((p.ascent() + p.descent()) / 2) + 1:字符起始位置的y坐标             * p:用到的画笔             * 关于涉及的矩形属性可看博客  http://mikewang.blog.51cto.com/3826268/871765             */            c.drawText(text, -b.left + 1,                    (nHeight / 2) - ((p.ascent() + p.descent()) / 2) + 1, p);                        return bm;        }        /**         * 重头戏         * 如果线程是运行的,SurfaceView也创建了的         * 则锁定画布com/mjpeg/io/MjpegInputStream.java中的readMjpegFrame方法获得mjpeg视频流的内容         * mjpeg视频的内容就是类位图,然后根据类位图绘制矩形,再绘制相应的位图,这个位图才是我们需要的         * 如果设置了帧率文本,就在mjpegview上覆盖,最后解锁画布         */        public void run() {            start = System.currentTimeMillis();            Rect destRect;            Paint p = new Paint();    //        String fps = "";            while (bRun) {                if (bsurfaceIsCreate) {                    c = mSurfaceHolder.lockCanvas();                    try {                        mjpegBitmap = mIs.readMjpegFrame();/*调用Inputstrean的方法*/                        /*同步图像的宽高设置*/                        synchronized (mSurfaceHolder) {                            destRect = destRect(mjpegBitmap.getWidth(),                                    mjpegBitmap.getHeight());                        }                        /**                         * 当主activity点击相册和设置跳转时,Surfaceview被销毁,此时c将为空                         */                        if(c != null){                            c.drawPaint(new Paint());                            c.drawBitmap(mjpegBitmap, null, destRect, p);                            if (bIsShowFps)                                calculateFps(destRect, c, p);                            mSurfaceHolder.unlockCanvasAndPost(c);                        }                    } catch (IOException e) {                    }                }else {                    try {                        Thread.sleep(500);//线程休眠,让出调度                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                }            }        }        /**         * 使用前面的方法,绘制出“显示帧率”文本,效果为"i帧",i自增         * @param destRect         * @param c         * @param p         */        public void calculateFps(Rect destRect, Canvas c, Paint p) {            int width;            int height;            String fps;                        p.setXfermode(mode);/* 设置两个画面相交时的模式*/            if (overlayBitmap != null) {                /**                 * 计算好文本的宽和高                 * 然后调用画布的绘制位图方法绘图                 */                height = ((ovlPos & 1) == 1) ? destRect.top                        : destRect.bottom - overlayBitmap.getHeight();                width = ((ovlPos & 8) == 8) ? destRect.left                         : destRect.right - overlayBitmap.getWidth();                c.drawBitmap(overlayBitmap, width, height, null);            }            p.setXfermode(null);            frameCounter++;            /**             * currentTimeMillis表示系统从January 1, 1970 00:00:00.0 UTC开始的毫秒数             * start在前面已经设置好,表示渲染线程开始的系统时间             */            if ((System.currentTimeMillis() - start) >= 1000) {                fps = frameCounter+ "fps";                start = System.currentTimeMillis();                overlayBitmap = makeFpsOverlay(overlayPaint, fps);/*真正的绘制这个"文本"*/                frameCounter = 0;                            }        }                    }}
复制代码

 

MjpegInputStream.java

复制代码
package com.mjpeg.io;import java.io.BufferedInputStream;import java.io.ByteArrayInputStream;import java.io.DataInputStream;import java.io.IOException;import java.io.InputStream;import java.io.Serializable;import java.util.Properties;import org.apache.http.HttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.params.CoreConnectionPNames;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Parcelable;import android.util.Log;/** * 该类继承了DataInputStream实现了Serializable接口 * 1. 实例化流,获取初始化流和关闭实例流的方法 * 2. 一个构造函数 * 3. 一个根据帧数据大小获得位图方法 */public class MjpegInputStream extends DataInputStream implements Serializable{    /**     *      */    private static final long serialVersionUID = 1L;    /**     * 用UE打开发现 每一个jpg格式的图片 开始两字节都是 0xFF,0xD8     */    private final byte[] SOI_MARKER = { (byte) 0xFF, (byte) 0xD8 };//    private final byte[] EOF_MARKER = { (byte) 0xFF, (byte) 0xD9 };    /**     * 表示服务器发给客户端的一帧数据的长度     */    private final String CONTENT_LENGTH = "Content-Length";    private final static int HEADER_MAX_LENGTH = 100;    private final static int FRAME_MAX_LENGTH = 40000 + HEADER_MAX_LENGTH;    private int mContentLength = -1;    private static MjpegInputStream mis = null;    /**     * 调用该类的构造方法 创建MjpegInputStream流     * @param is     */    public static void initInstance(InputStream is){        if(mis == null)            mis = new MjpegInputStream(is);            }    /**     * 获得创建的mjpegInputsteam流     * @return     */    public static MjpegInputStream getInstance(){        if(mis != null)            return mis;                return null;    }    /**     * 因为mpjeginputstream继承了datainputstream     * 所以可以调用mpjeginputstream的关闭流方法     */    public static void closeInstance(){        try {            mis.close();        } catch (IOException e) {            e.printStackTrace();        }        mis = null;    }    private MjpegInputStream(InputStream in) {        super(new BufferedInputStream(in, FRAME_MAX_LENGTH));    }    /**     * 在数据流里面找SOI_MARKER={(byte)0xFF,(byte) 0xD8}     * 所有对IO流的操作都会抛出异常     * @param in     * @param sequence     * @return     * @throws IOException     */    private int getEndOfSeqeunce(DataInputStream in, byte[] sequence)            throws IOException {        int seqIndex = 0;        byte c;        for (int i = 0; i < FRAME_MAX_LENGTH; i++) {// 0 1 2 3            c = (byte) in.readUnsignedByte();            if (c == sequence[seqIndex]) {                seqIndex++;                if (seqIndex == sequence.length)//2                    return i + 1;//3            } else                seqIndex = 0;        }        return -1;    }    /**     * 此方法功能是找到索引0xFF,0XD8在字符流的位置     * 整个数据流形式:http头信息 帧头(0xFF 0xD8) 帧数据 帧尾(0xFF 0xD9)     * 1、首先通过0xFF 0xD8找到帧头位置     * 2、帧头位置前的数据就是http头,里面包含Content-Length,这个字段指示了整个帧数据的长度     * 3、帧头位置后面的数据就是帧图像的开始位置     * @param in     * @param sequence     * @return     * @throws IOException     */    private int getStartOfSequence(DataInputStream in, byte[] sequence)            throws IOException {        int end = getEndOfSeqeunce(in, sequence);        return (end < 0) ? (-1) : (end - sequence.length);    }    /**     * 从http的头信息中获取Content-Length,知道一帧数据的长度     * @param headerBytes     * @return     * @throws IOException     * @throws NumberFormatException     */    private int parseContentLength(byte[] headerBytes) throws IOException,            NumberFormatException {        /**         * 根据字节流创建ByteArrayInputStream流         * Properties是java.util包里的一个类,它有带参数和不带参数的构造方法,表示创建无默认值和有默认值的属性列表         * 根据流中的http头信息生成属性文件,然后找到属性文件CONTENT_LENGTH的value,这就找到了要获得的帧数据大小         * 创建一个 ByteArrayInputStream,使用 headerBytes作为其缓冲区数组         */        ByteArrayInputStream headerIn = new ByteArrayInputStream(headerBytes);        Properties props = new Properties();/*创建一个无默认值的空属性列表*/        props.load(headerIn);/*从输入流中生成属性列表(键和元素对)。*/        return Integer.parseInt(props.getProperty(CONTENT_LENGTH));/*用指定的键在此属性列表中搜索属性。*/    }    /**     *      * @return     * @throws IOException     */    public Bitmap readMjpegFrame() throws IOException {        mark(FRAME_MAX_LENGTH);/*流中当前的标记位置*/        int headerLen = getStartOfSequence(this, SOI_MARKER);        reset();/*将缓冲区的位置重置为标记位置*/        byte[] header = new byte[headerLen];        readFully(header);/*会一直阻塞等待,直到数据全部到达(数据缓冲区装满)*///        String s = new String(header);        try {            mContentLength = parseContentLength(header);// ?        } catch (NumberFormatException e) {            return null;        }        /**         * 根据帧数据的大小创建字节数组         */        byte[] frameData = new byte[mContentLength];        readFully(frameData);        /**         * 根据不同的源(file,stream,byte-arrays)创建位图         * 把输入字节流流转为位图         */        return BitmapFactory.decodeStream(new ByteArrayInputStream(frameData));    }}
复制代码

 

WsnActivty.java

复制代码
package my.work.Library;import java.util.Timer;import java.util.TimerTask;import java.util.regex.Matcher;import java.util.regex.Pattern;import android.app.Activity;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.EditText;import android.widget.ImageButton;import android.widget.ImageView;import android.widget.TextView;public class WsnActivty extends Activity {    /** Called when the activity is first created. */    private Button btnNetwork,btnVideo;    private String strIpAddr = null;    static TextView textTips,seat;    private ClientThread clientThread = null;    private Message MainMsg;    public static Handler mainHandler;    static final int TIPS_UPDATE_UI = 3;   //tips_update_ui    static final int SEAT_UPDATE_UI = 6;   //seat_update_ui        static final int MAX_NODE = 4;    static byte NodeData[][] = new byte[MAX_NODE][5];; // [5] 0=温度 1=湿度 2=气体 3=灯    static final int RX_DATA_UPDATE_UI = 1;            @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        initControl();        initMainHandler();      }    private void initControl() {        // TODO Auto-generated method stub          btnNetwork = (Button) findViewById(R.id.btn_network);          btnNetwork.setOnClickListener(new ButtonClick());                    textTips = (TextView) findViewById(R.id.Tips);            textTips.setText(R.string.init_tips);                        seat = (TextView) findViewById(R.id.statc001);            seat.setText(R.string.people1);                        btnVideo = (Button) findViewById(R.id.btn_video);          btnVideo.setOnClickListener(new ButtonClick());            }    class ButtonClick implements OnClickListener {        @Override        public void onClick(View v) {                        switch (v.getId()) {            case R.id.btn_network: // 连接网络                showDialog(WsnActivty.this);                break;            case R.id.btn_video: // 暂时用作停止自动刷新功能  ,跳转到FlashActivity去                // mainTimer.cancel(); //看视频关闭定时查询数据 要判断                if (clientThread != null) {                    MainMsg = ClientThread.childHandler                            .obtainMessage(ClientThread.RX_EXIT);  //关闭线程                    ClientThread.childHandler.sendMessage(MainMsg);                }                 /*new一个Intent对象,并指定class*/                 Intent intent = new Intent();                  intent.setClass(WsnActivty.this,FlashActivity.class);                                    /*new一个Bundle对象,并将要传递的数据传入*/                 Bundle bundle = new Bundle();                  bundle.putString("IP",strIpAddr);                  /*将Bundle对象assign给Intent*/                 intent.putExtras(bundle);                                  /*调用Activity FlashActivity*/                 startActivity(intent);                 //startActivity(new Intent(WsnActivity.this, FlashActivity.class));//简单跳转                break;                }   }    }            // 显示连接对话框            private void showDialog(Context context) {                final EditText editIP = new EditText(context);                editIP.setText("192.168.0.10");                AlertDialog.Builder builder = new AlertDialog.Builder(context);                // builder.setIcon(R.drawable.ic_launcher);                builder.setTitle("请输入服务器IP地址");                builder.setView(editIP);                builder.setPositiveButton("连接", new DialogInterface.OnClickListener() {                    public void onClick(DialogInterface dialog, int whichButton) {                        strIpAddr = editIP.getText().toString();                        boolean ret = isIPAddress(strIpAddr);                        if (ret) {                            textTips.setText("服务器IP地址:" + strIpAddr);                        } else {                            strIpAddr = null;                            textTips.setText("IP地址不合法,请重新设置");                            return;                        }                        clientThread = new ClientThread(strIpAddr);// 建立客户端线程                        clientThread.start();                        //mainTimer = new Timer();// 定时查询所有终端信息                        //setTimerTask();                    }                });                builder.setNeutralButton("取消", new DialogInterface.OnClickListener() {                    public void onClick(DialogInterface dialog, int whichButton) {                        if (clientThread != null) {                            MainMsg = ClientThread.childHandler                                    .obtainMessage(ClientThread.RX_EXIT);                            ClientThread.childHandler.sendMessage(MainMsg);                            textTips.setText("与服务器断开连接");                        }                    }                });                builder.show();            }                // 判断输入IP是否合法        private boolean isIPAddress(String ipaddr) {            boolean flag = false;            Pattern pattern = Pattern                    .compile("\\b((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\.((?!\\d\\d\\d)\\d+|1\\d\\d|2[0-4]\\d|25[0-5])\\b");            Matcher m = pattern.matcher(ipaddr);            flag = m.matches();            return flag;        }                void initMainHandler() {            mainHandler = new Handler() {                // 主线程消息处理中心                public void handleMessage(Message msg) {                    switch (msg.what) {                       case TIPS_UPDATE_UI:                        String str = (String) msg.obj;  //连接成功                        textTips.setText(str);                        break;                       case SEAT_UPDATE_UI:                            String strseat = (String) msg.obj;  //rfid                                                        seat.setText(strseat);                            break;                    }                    super.handleMessage(msg);                }            };        }}    
复制代码

 

ClientThread.java

复制代码
package my.work.Library;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetSocketAddress;import java.net.Socket;import java.net.SocketAddress;import android.content.Context;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.widget.Toast;public class ClientThread extends Thread {    private OutputStream outputStream = null;    private InputStream inputStream = null;    private Socket socket;    private SocketAddress socketAddress;    public static Handler childHandler;    private boolean RxFlag = true;    final int TEXT_INFO = 12;    static final int RX_EXIT = 11;    static final int TX_DATA = 10;    Context mainContext;    Message msg;    private String strIP;    final int SERVER_PORT = 33333;    byte cNodeData[][] = new byte[4][5]; // [5] 0=温度 1=湿度 2=气体 3=灯    int RxCount = 0, nRecvLen, index = 0;    byte CheckSum;    // byte strRxBuf[] = new byte[256];    int ucRecvLen = 7;        private RxThread rxThread;    //获取WsnActivty.java 开辟子线程clientThread对象线程传递过来的ip地址    public ClientThread(String ip) {        strIP = ip;    }    // 连接网络    void connect() {        RxFlag = true;        socketAddress = new InetSocketAddress(strIP, SERVER_PORT);        socket = new Socket();        try {            socket.connect(socketAddress, SERVER_PORT);            inputStream = socket.getInputStream();            outputStream = socket.getOutputStream();            msg = WsnActivty.mainHandler.obtainMessage(                    WsnActivty.TIPS_UPDATE_UI, "连接成功");            WsnActivty.mainHandler.sendMessage(msg);                                    rxThread = new RxThread();            rxThread.start();        } catch (IOException e) {            try {                sleep(10);            } catch (InterruptedException e1) {                e1.printStackTrace();            }            msg = WsnActivty.mainHandler.obtainMessage(                    WsnActivty.TIPS_UPDATE_UI, "无法连接到服务器");            WsnActivty.mainHandler.sendMessage(msg);            e.printStackTrace();        } catch (NumberFormatException e) {        }    }    void initChildHandler() {        Looper.prepare(); // 在子线程中创建Handler必须初始化Looper        childHandler = new Handler() {            // 子线程消息处理中心            public void handleMessage(Message msg) {                // 接收主线程及其他线程的消息并处理...                /**                 * MainMsg = ClientThread.childHandler.obtainMessage(ClientThread.TX_DATA,                 * len, 0, (Object) buffer);                 * SendData(SendBuf, 7);                 */                switch (msg.what) {                                case RX_EXIT:                    RxFlag = false;                    try {                        if (socket.isConnected()) {                            inputStream.close();                            outputStream.close();                            socket.close();                        }                    } catch (IOException e1) {                        e1.printStackTrace();                    }                    childHandler.getLooper().quit();// 结束消息队列                    break;                default:                    break;                }            }        };        // 启动该线程的消息队列        Looper.loop();    }    public void run() {        connect();        initChildHandler();        msg = WsnActivty.mainHandler.obtainMessage(WsnActivty.TIPS_UPDATE_UI,                "与服务器断开连接");        WsnActivty.mainHandler.sendMessage(msg);    }        // socket 接收线程    public class RxThread extends Thread {        public void run() {            try {                while (socket.isConnected() && RxFlag) {                    byte strRxBuf[] = new byte[30];                                        byte i;                    int RxIndex, len, readBytes = 0;                                            while (readBytes < ucRecvLen) {           //接收到数据,存放到strRxBuf                        len = inputStream.read(strRxBuf,readBytes, 8);                        readBytes += len;                                                if (len == -1)                            break;                        }                                                                    String strRead =new String(strRxBuf);                        String str=new String("822350C2");                        String str1="822350C2";                                                if(str.equals(str1))                        {                        msg = WsnActivty.mainHandler.obtainMessage(                                WsnActivty.SEAT_UPDATE_UI, "有人");                        WsnActivty.mainHandler.sendMessage(msg);                        }                        msg = WsnActivty.mainHandler.obtainMessage(                                WsnActivty.SEAT_UPDATE_UI, strRead);                        WsnActivty.mainHandler.sendMessage(msg);                                                            }                if (socket.isConnected())                    socket.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }        byte GetDataLen(byte fc) {        byte len = 0;        switch (fc) {        case 0x01:            len = 22;            break;        case 0x07:        case 0x08:        case 0x0A:        case 0x0B:        case 0x0C:        case 0x0D:            len = 7;            break;        }        return len;    }    }
复制代码

 

FlashActivity.java

复制代码
package my.work.Library;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import my.work.Library.WsnActivty.ButtonClick;import org.apache.http.HttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.params.CoreConnectionPNames;import tools.Generic;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.content.SharedPreferences;import android.content.SharedPreferences.Editor;import android.net.DhcpInfo;import android.net.wifi.WifiManager;import android.os.AsyncTask;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.ArrayAdapter;import android.widget.AutoCompleteTextView;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import com.mjpeg.io.MjpegInputStream;/** * 应用程序执行时,该类首先被调用 */public class FlashActivity extends Activity {    private Context mContext = this;    private EditText ipEdt = null;    private EditText portEdt = null;    private TextView hintTv = null;    private DhcpInfo dpInfo = null;    private WifiManager wifi = null;    private InputStream is = null;    private SharedPreferences sp = null;    private Editor editor = null;    private String port = "8080";/* 用来保存获得用户输入的端口 */    private Bundle bundle;    private Button connectin;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.flash);/* 设置布局为res/layout/flash.xml*/        init();        int state = wifi.getWifiState();/* 获得wifi当前状态 */                if (state != WifiManager.WIFI_STATE_ENABLED) {            /**             * 为了程序的扩展性和可读性,单独在tools目录定义一个Generic类,它有很多方法             * 1.有showMsg方法,用于控制显示时间来显示一个Toast             * 2.有getSysNowTime方法,用于获取当前的系统时间             * 3.有getSdCardFile方法,用于获取SD卡的绝对路径,成功返回File值,失败返回NULL             * 4.有getConnectedIP方法,用于获取连接到wifi热点的所有的手机ip,成功返回ArrayList<String>型的容器             * 5.有getShrinkedPic方法,用于获取照片的缩略图             * 6.定义了一个DescendSortByIndex类:实现了整型比较器             * 7.定义个DescendSortByTime类:实现了File比较器             */            Generic.showMsg(this, "请打开wifi", false);            finish();        } else             /* 取得Intent中的Bundle对象 */             bundle = this.getIntent().getExtras();                            /* 取得Bundle对象中的数据 */             String strIP = bundle.getString("IP");                          //autoConnect(strIP);    }    @Override    /**     * 调用finish方法时,这方法将被激发     * 设置输入流为空,调用父类的onDestroy销毁资源     */    protected void onDestroy() {        is = null;        super.onDestroy();    }    private void init() {        /**         * 获取在本Activity要使用的控件和WiFi         */        hintTv = (TextView) findViewById(R.id.hintTv);        ipEdt = (EditText) findViewById(R.id.ip);        portEdt = (EditText) findViewById(R.id.port);                connectin = (Button) findViewById(R.id.connect);        connectin.setOnClickListener(new ButtonClick());                         /**         * 因为要用到WIFI和Internet所以在AndroidMenufest.xml 中添加如下权限 <uses-permission         * android:name="android.permission.INTERNET"/> <uses-permission         * android:name="android.permission.ACCESS_WIFI_STATE"/>         * <uses-permission         * android:name="android.permission.CHANGE_WIFI_STATE"/>         */        wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);        //initSp();/* 主要是方便查找以前登录成功了的IP */    }    class ButtonClick implements OnClickListener {        @Override        public void onClick(View v) {                        String ip = ipEdt.getText().toString();/* 获得输入的IP */            port = portEdt.getText().toString();/* 获得输入的端口 */            // port不能为空,ip地址格式正确            if (!port.equals("") && checkAddr(ip, Integer.valueOf(port))) {                new ConnectTask().execute(ip);            } else {                Generic.showMsg(mContext, "请检查ip或port", true);            }            }        }//    /**//     * 生成配置文件config,它在 /data/data/<package name>/shared_prefs/config.xml//     * 取出配置文件的ip用冒号隔开,并为自动完成列表设置适配器//     *///    private void initSp() {//        sp = getSharedPreferences("config", MODE_PRIVATE);//        /* 创建好配置文件后,以后就可以用它的edit来操作配置文件了 *///        editor = sp.edit();//        String names[] = sp.getString("ip", "").split(":");//        ArrayAdapter<String> adapter = new ArrayAdapter<String>(mContext,//                android.R.layout.simple_dropdown_item_1line, names);//        //ipEdt.setAdapter(adapter);//    }////    /**//     * 自动连接 先将获取到的wifi热点服务器地址和连接到wifi热点的设备的ip放入容器,启动连接线程扫描容器中的ip//     * //     * @return//     *///    private void autoConnect(String strIP) {//        ArrayList<String> addr = new ArrayList<String>();/* 创建容器 用于存放ip */////        dpInfo = wifi.getDhcpInfo();//        addr.add(int32ToIp(dpInfo.serverAddress));/* 把服务IP放入容器的尾部 *///        addr.addAll(Generic.getConnectedIP());// Adds the objects in the specified collection to this ArrayList////        // 为了在执行连接时 不会卡住UI,故采用异步任务方式,若读者想减缩程序,也可不使用异步任务//        if (strIP != null) {//            new ConnectTask().execute(strIP);//        } else {//            //因为连接线程的执行方法必须String类型,所以要toArray    //            new ConnectTask().execute(addr.toArray(new String[addr.size()]));//        }//    }    /**     * 按照一定的格式返回输入的Ip     *      * @param ip     * @return     */    private String int32ToIp(int ip) {        return (ip & 0xff) + "." + (ip >> 8 & 0xff) + "." + (ip >> 16 & 0xff)                + "." + (ip >> 24 & 0xff);    }//    /**//     * 手动连接 为控件绑定监听器有2种方法 1.给出布局文件并设置,findViewById()找到控件,调用API为其绑定相应监听器//     * 2.给出布局文件并设置,在布局文件里设置相应控件的OnClick,然后在源文件里具体实现相应控件的OnClick//本类用的就是这方法//     * 在layout目录下的flash.xml里声明了connectBtn的Button控件 点击"连接"按钮将调用此方法//     * //     * @param v//     *///    public void connectBtn() {//        String ip = ipEdt.getText().toString();/* 获得输入的IP *///        port = portEdt.getText().toString();/* 获得输入的端口 */////        // port不能为空,ip地址格式正确//        if (!port.equals("") && checkAddr(ip, Integer.valueOf(port))) {//            new ConnectTask().execute(ip);//        } else {//            Generic.showMsg(mContext, "连接失败", true);//        }//    }    /**     * 分割的ip是4段,ip端口范围在1000-65535     *      * @param ip     * @param port     * @return     */    private boolean checkAddr(String ip, int port) {        if (ip.split("\\.").length != 4)            return false;        if (port < 1000 || port > 65535)            return false;        return true;    }    /**     * 连接线程 此类的作用是在后台线程里执行http连接,连接卡住不会影响UI运行,适合于运行时间较长但又不能影响前台线程的情况     * 异步任务,有3参数和4步     * :onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()     * onPreExecute():运行于UI线程,一般为后台线程做准备,如在用户接口显示进度条     * doInBackground():当onPreExecute执行后,马上被触发,执行花费较长时间的后台运算,将返回值传给onPostExecute     * onProgressUpdate():当用户调用 publishProgress()将被激发,执行的时间未定义,这个方法可以用任意形式显示进度     * 一般用于激活一个进度条或者在UI文本领域显示logo onPostExecute():当后台进程执行后在UI线程被激发,把后台执行的结果通知给UI     * 参数一:运行于后台的doInBackground的参数类型     * 参数二:doInBackground计算的通知给UI线程的单元类型,即运行于UI线程onProgressUpdate的参数类型,这里没用到     * 参数三:doInBackground的返回值,将传给onPostExecute作参数     *      * @author Administrator     *      */    private class ConnectTask extends AsyncTask<String, Integer, String> {        @Override        protected String doInBackground(String... params) {            for (int i = 0; i < params.length; i++) {                String ip = params[i];/* 取出每一个ip */                if (ip.split("\\.").length == 4) {                    /**                     * 在浏览器观察画面时,也是输入下面的字符串网址                     */                    String action = "http://" + ip + ":" + port                            + "/?action=stream";                    is = http(action);                    if (is != null) { /* 第一次必须输入IP,下次登录时才可找到之前登录成功后的IP */                        //writeSp(ip);                        MjpegInputStream.initInstance(is);  //消息实体内容is                        break;                    }                }            }            return null;        }        @Override        protected void onPostExecute(String result) {            if (is != null) {                /**                 * Intent是Android特有的东西,可以在Intent指定程序要执行的动作(比如:view,edit,dial)                 * 都准备好程序执行该工作所需要的材料后                 * ,只要调用startActivity,Android系统会自动寻找最符合你指定要求的应用程序 并执行该程序                 */                startActivity(new Intent(FlashActivity.this, LinearLayout_activity.class));                finish();/* 结束本Activity */            } else {                hintTv.setText(getResources()                        .getString(R.string.connect_failed));                Generic.showMsg(mContext, "连接失败", true);            }            super.onPostExecute(result);        }        /**         * 功能:http连接 Android提供两种http客户端, HttpURLConnection 和 Apache HTTP         * Client,它们都支持HTTPS,能上传和下载文件 配置超时时间,用于IPV6和 connection pooling, Apache         * HTTP client在Android2.2或之前版本有较少BUG         * 但在Android2.2或之后,HttpURLConnection是更好的选择,在这里我们用的是 Apache HTTP Client         * 凡是对IO的操作都会涉及异常,所以要try和catch         *          * @param url         * @return InputStream         */        private InputStream http(String url) {            HttpResponse res;            DefaultHttpClient httpclient = new DefaultHttpClient();/*                                                                     * 创建http客户端,                                                                     * 才能调用它的各种方法                                                                     */            httpclient.getParams().setParameter(                    CoreConnectionPNames.CONNECTION_TIMEOUT, 500);/* 设置超时时间 */            try {                HttpGet hg = new HttpGet(url);/*                                             * 这是GET方法的http API,                                             * GET方法是默认的HTTP请求方法                                             */                res = httpclient.execute(hg);                return res.getEntity().getContent(); // 从响应中获取消息实体内容            } catch (IOException e) {            }            return null;        }    }}//    /**//     * 更新SharedPreferences 1.先判断ip是否有"ip"值,没有就将传进来的data赋值给ip 2.ip有值就取出,然后用冒号分隔开//     * 3.sp数组只能存放10组ip,如果超过了10组,先清零配置文件再更新 4.遍历数组,如果已有当前登录成功的ip,则返回//     * 5.数组里不包含登录成功的ip,则将当前登录成功的ip添加至sp数组并提交//     * //     * @param ip//     *///    private void writeSp(String data) {//        if (!sp.contains("ip")) {//            editor.putString("ip", data);//            editor.commit();//            return;//        }////        /**//         * 配置文件里有ip,表示之前登录成功了//         *///        String ip = sp.getString("ip", "");//        String[] ips = ip.split(":");////        if (ips.length >= 10) {//            editor.clear();//            editor.commit();//            editor.putString("ip", data);//            editor.commit();//            return;//        }////        for (int i = 0; i < ips.length; i++) {//            if (ips[i].equals(data))//                return;//        }//        editor.putString("ip", data + ":" + ip);/* 放在以前成功了的ip的前面 *///        editor.commit();//    }////    /**//     * 自动完成框的下拉选项 当点击"history_user"ImageView控件时将调用该方法 这里只是具体实现xml文件的Onclick//     *///    public void showDropDown(View v) {//        ipEdt.showDropDown();//    }
复制代码

 

LinearLayout_activity.java

复制代码
package my.work.Library;import com.mjpeg.io.MjpegInputStream;import com.mjpeg.view.MjpegView;import android.app.Activity;import android.os.Bundle;import my.work.Library.R;public class LinearLayout_activity extends Activity {    public static LinearLayout_activity instance = null;    private MjpegInputStream mis = null;    private MjpegView mjpegView = null;    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.mainactivty);/*构造RadioGroup的5的RadioButton*/        instance = this;        mis = MjpegInputStream.getInstance();        mjpegView = (MjpegView) findViewById(R.id.mjpegview);                initMjpegView();    }        private void initMjpegView() {        if (mis != null) {            mjpegView.setSource(mis);// 设置数据来源            mjpegView.setDisplayMode(mjpegView.getDisplayMode());/*设置mjpegview的显示模式*/            /**             * setFps和getFps方法是为了在屏幕的右上角动态显示当前的帧率             * 如果我们只需观看画面,下面这句完全可以省去             */            mjpegView.setFps(mjpegView.getFps());            /**             * 调用mjpegView中的线程的run方法,开始显示画面             */            mjpegView.setDisplayMode(MjpegView.FULLSCREEN_MODE);/*全屏*/            mjpegView.startPlay();        }    }}
复制代码


原创粉丝点击