由于Mjpeg流是通過一幀一幀的圖片發(fā)送來達(dá)到視頻顯示的效果,所以我們用類似網(wǎng)絡(luò)加載圖片方式加載,但是由于這個流是一直在發(fā)送的,我們需要知道這一幀的圖片流的開始位置和結(jié)束位置喉钢,才能顯示這一幀的圖片,原理就是這樣的良姆,其他就直接上代碼了
這個類是網(wǎng)上找到的肠虽,搜android加載mjpeg流基本可以搜到,基本都有說明
package com.stereo.video.utils.mjpeg;
/**
* Created by Administrator on 2018/8/22 0022.
*/
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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;
/**
* 該類繼承了DataInputStream實現(xiàn)了Serializable接口
* 1. 實例化流,獲取初始化流和關(guān)閉實例流的方法
* 2. 一個構(gòu)造函數(shù)
* 3. 一個根據(jù)幀數(shù)據(jù)大小獲得位圖方法
*/
public class MjpegInputStream extends DataInputStream implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* 用UE打開發(fā)現(xiàn) 每一個jpg格式的圖片 開始兩字節(jié)都是 0xFF,0xD8
*/
private final byte[] SOI_MARKER = {(byte) 0xFF, (byte) 0xD8};
private final byte[] EOF_MARKER = { (byte) 0xFF, (byte) 0xD9 };
/**
* 表示服務(wù)器發(fā)給客戶端的一幀數(shù)據(jù)的長度
*/
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;
/**
* 調(diào)用該類的構(gòu)造方法 創(chuàng)建MjpegInputStream流
*
* @param is
*/
public static void initInstance(InputStream is) {
if (mis == null)
mis = new MjpegInputStream(is);
}
/**
* 獲得創(chuàng)建的mjpegInputsteam流
*
* @return
*/
public static MjpegInputStream getInstance() {
if (mis != null)
return mis;
return null;
}
/**
* 因為mpjeginputstream繼承了datainputstream
* 所以可以調(diào)用mpjeginputstream的關(guān)閉流方法
*/
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));
}
/**
* 在數(shù)據(jù)流里面找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在字符流的位置
* 整個數(shù)據(jù)流形式:http頭信息 幀頭(0xFF 0xD8) 幀數(shù)據(jù) 幀尾(0xFF 0xD9)
* 1、首先通過0xFF 0xD8找到幀頭位置
* 2玛追、幀頭位置前的數(shù)據(jù)就是http頭税课,里面包含Content-Length闲延,這個字段指示了整個幀數(shù)據(jù)的長度
* 3、幀頭位置后面的數(shù)據(jù)就是幀圖像的開始位置
*
* @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韩玩,知道一幀數(shù)據(jù)的長度
*
* @param headerBytes
* @return
* @throws IOException
* @throws NumberFormatException
*/
private int parseContentLength(byte[] headerBytes) throws IOException,
NumberFormatException {
/**
* 根據(jù)字節(jié)流創(chuàng)建ByteArrayInputStream流
* Properties是java.util包里的一個類垒玲,它有帶參數(shù)和不帶參數(shù)的構(gòu)造方法,表示創(chuàng)建無默認(rèn)值和有默認(rèn)值的屬性列表
* 根據(jù)流中的http頭信息生成屬性文件啸如,然后找到屬性文件CONTENT_LENGTH的value侍匙,這就找到了要獲得的幀數(shù)據(jù)大小
* 創(chuàng)建一個 ByteArrayInputStream,使用 headerBytes作為其緩沖區(qū)數(shù)組
*/
ByteArrayInputStream headerIn = new ByteArrayInputStream(headerBytes);
Properties props = new Properties();/*創(chuàng)建一個無默認(rèn)值的空屬性列表*/
props.load(headerIn);/*從輸入流中生成屬性列表(鍵和元素對)叮雳。*/
String parse = props.getProperty(CONTENT_LENGTH); //這個位置如果錯誤記得看一下想暗,注意大小寫對比
return Integer.parseInt(parse);/*用指定的鍵在此屬性列表中搜索屬性。*/
}
/**
* @return
* @throws IOException
*/
public Bitmap readMjpegFrame() throws IOException {
mark(FRAME_MAX_LENGTH);/*流中當(dāng)前的標(biāo)記位置*/
int headerLen = getStartOfSequence(this, SOI_MARKER);
reset();/*將緩沖區(qū)的位置重置為標(biāo)記位置*/
byte[] header = new byte[headerLen];
readFully(header);/*會一直阻塞等待帘不,直到數(shù)據(jù)全部到達(dá)(數(shù)據(jù)緩沖區(qū)裝滿)*/
mContentLength = parseContentLength(header);// ?
/**
* 根據(jù)幀數(shù)據(jù)的大小創(chuàng)建字節(jié)數(shù)組
*/
byte[] frameData = new byte[mContentLength];
readFully(frameData);
/**
* 根據(jù)不同的源(file说莫,stream,byte-arrays)創(chuàng)建位圖
* 把輸入字節(jié)流流轉(zhuǎn)為位圖
*/
Bitmap bitmap =
BitmapFactory.decodeStream(new ByteArrayInputStream(frameData));/*方便查看圖片是否解析出來*/
return bitmap;
}
}
這個是調(diào)用后臺接口地址寞焙,url就是 mjpeg的http地址储狭,記得子線程調(diào)用
private Bitmap getImageBitmap(String url) {
URL imgUrl = null;
Bitmap bitmap = null;
try {
Log.v("bitmapfactory", "URL");
imgUrl = new URL(url);
conn = (HttpURLConnection) imgUrl.openConnection();
conn.setDoInput(true);
conn.connect();
Log.v("bitmapfactory", "connect");
InputStream inputStream = conn.getInputStream();
Log.v("bitmapfactory", "getInputStream:");
BufferedInputStream bis = new BufferedInputStream(inputStream);
DataInputStream dis= new DataInputStream(bis);
MjpegInputStream.initInstance(dis);
MjpegInputStream mjpegInputStream = MjpegInputStream.getInstance();
// Bitmap bmp = mjpegInputStream.readMjpegFrame();
Message msg = new Message();
msg.what = 1;
msg.obj = mjpegInputStream;
handler.sendMessage(msg);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
Log.v("bitmapfactory:", "MalformedURLException");
e.printStackTrace();
} catch (IOException e) {
Log.v("bitmapfactory:", "IOException");
if (conn != null) {
conn.disconnect();
}
e.printStackTrace();
}
return bitmap;
}
然后通過handler接收到流然后傳入流,然后開始就可以了
mjpegView.setSource((MjpegInputStream) msg.obj);
mjpegView.startPlay();
MjpegView捣郊,這個也是網(wǎng)上的代碼辽狈,如果需要自己拿到Bitamap可以直接在調(diào)用網(wǎng)絡(luò)接口位置調(diào)用流解析代碼,就可以拿到bitmap呛牲,然后bitmap設(shè)置到其他位置刮萌,MjpegView也是調(diào)用解析流的工具類拿到bitmap然后繪制
package com.stereo.video.utils.mjpeg;
/**
* Created by Administrator on 2018/8/22 0022.
*/
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
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實現(xiàn)了SurfaceHolder.Callback接口
* SurfaceView是視圖類(view)的繼承類,這個視圖里內(nèi)嵌入了一個專門用于繪制的Surface 娘扩,可以控制這個Surface的格式和尺寸
* SurfaceView控制這個Surface的繪制位置
* surface是縱深排序(Z-ordered)的着茸,這表明它總在自己所在窗口的后面。surfaceview提供了一個可見區(qū)域
* 只有在這個可見區(qū)域內(nèi) 的surface部分內(nèi)容才可見琐旁,可見區(qū)域外的部分不可見涮阔。surface的排版顯示受到視圖層級關(guān)系的影響
* 它的兄弟視圖結(jié)點會在頂端顯示,這意味者 surface的內(nèi)容會被它的兄弟視圖遮擋灰殴,這一特性可以用來放置遮蓋物(overlays)(例如敬特,文本和按鈕等控件)
* 可以通過SurfaceHolder接口訪問這個surface,getHolder()方法可以得到這個接口
* surfaceview變得可見時 牺陶,surface被創(chuàng)建擅羞;surfaceview隱藏前,surface被銷毀义图;這樣能節(jié)省資源减俏。如果你要查看 surface被創(chuàng)建和銷毀的時機(jī)
* 可以重載surfaceCreated(SurfaceHolder)和 surfaceDestroyed(SurfaceHolder)
* surfaceview的核心在于提供了兩個線程:UI線程和渲染線程,這里應(yīng)注意:
* 1> 所有SurfaceView和SurfaceHolder.Callback的方法都應(yīng)該在UI線程里調(diào)用碱工,一般來說就是應(yīng)用程序主線程娃承,渲染線程所要訪問的各種變量應(yīng)該作同步處理奏夫。
* 2> 由于surface可能被銷毀,它只在SurfaceHolder.Callback.surfaceCreated()和 SurfaceHolder.Callback.surfaceDestroyed()之間有效历筝,
* 所以要確保渲染線程訪問的是合法有效的surface
* 整個過程:繼承SurfaceView并實現(xiàn)SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()獲得SurfaceHolder對象(Surface控制器)
* ---->SurfaceHolder.addCallback(callback)添加回調(diào)函數(shù)---->SurfaceHolder.lockCanvas()獲得Canvas對象并鎖定畫布
* ----> Canvas繪畫 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)結(jié)束鎖定畫圖酗昼,并提交改變,將圖形顯示梳猪。
*/
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;//標(biāo)準(zhǔn)尺寸
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中作為自定義的控件使用了這個類麻削,所以需要給此類提供帶有屬性形參的構(gòu)造函數(shù)
* 當(dāng)在MainActivity通過ID找到這自定義的控件時,該構(gòu)造函數(shù)將被調(diào)用春弥,所以將該構(gòu)造函數(shù)設(shè)為public
*
* @param context
* @param attrs
*/
public MjpegView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
/**
* 類的私有方法
* 1.獲得Surface控制器呛哟,為Surface控制器添加回調(diào)接口
* 2.新建渲染線程MjpegViewThread
* 3.新建覆蓋畫筆,設(shè)置文本的對齊方式匿沛、文本長度扫责、字體劳跃、畫筆文本顏色舅逸、畫筆背景
* 4.設(shè)置覆蓋動態(tài)文本的覆蓋位置 //如果你只需要實現(xiàn)監(jiān)控畫面的功能膨桥,3和4步可以省略
* 5.設(shè)置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的任何結(jié)構(gòu)性結(jié)構(gòu)性的改變(如格式沸枯,大小)將激發(fā)此方法
* 主要調(diào)用渲染線程的setSurfaceSize來設(shè)置Surface的寬和高
*/
public void surfaceChanged(SurfaceHolder holder, int f, int w, int h) {
mvThread.setSurfaceSize(w, h);
}
/**
* Surface被銷毀之前將激發(fā)此方法,這里只設(shè)置標(biāo)記位畔柔,表示Surface“被銷毀了”
*/
public void surfaceDestroyed(SurfaceHolder holder) {
bsurfaceIsCreate = false;
}
/**
* Surface被第一次創(chuàng)建后將激發(fā)此方法仔戈,這里只設(shè)置標(biāo)記位柠横,表示Surface“被創(chuàng)建了”
*/
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;
}
/**
* 開始播放線程
* 設(shè)置標(biāo)記,表示“Surface被創(chuàng)建了”拾碌,然后調(diào)用渲染線程的的run方法啟動渲染
*/
public void startPlay() {
if (mIs != null) {
bRun = true;
mvThread.start();
}
}
/**
* 停止播放線程
* 1.先設(shè)置標(biāo)記,表示"停止播放"
* 2.等待播放線程的退出
* 3.關(guān)閉輸入流
*/
public void stopPlay() {
bRun = false;
boolean retry = true;
while (retry) {
try {
mvThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
//線程停止后關(guān)閉Mjpeg流(很重要)
mIs.closeInstance();
}
/**
* mjpegview的獲取位圖方法街望,調(diào)用渲染線程的獲取位圖方法
*
* @return
*/
public Bitmap getBitmap() {
return mvThread.getBitmap();
}
/**
* 設(shè)置顯示模式校翔,在MainActivity的initview調(diào)用
*
* @param s
*/
public void setDisplayMode(int s) {
displayMode = s;
}
/**
* 既然有設(shè)置顯示模式,就應(yīng)該也有獲得顯示模式灾前,這是java在設(shè)置方法方面的風(fēng)格
*
* @return
*/
public int getDisplayMode() {
return displayMode;
}
/**
* 此渲染線程類在主類上是重點防症,應(yīng)該重點掌握
*
* @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;
/**
* 用一個變量來保存?zhèn)鬟M(jìn)來的surfaceHolder
* 新建一個目的圖層和覆蓋圖層的相交模式,mjpegview為目的圖層哎甲,覆蓋圖層為右上角的動態(tài)"文本"
* mode在calculateFps方法里使用
*
* @param surfaceHolder:Surfaceview控制器
* @param context : 上下文環(huán)境
*/
public MjpegViewThread(SurfaceHolder surfaceHolder, Context context) {
mSurfaceHolder = surfaceHolder;
mode = new PorterDuffXfermode(PorterDuff.Mode.DST_OVER);/*相交時動態(tài)文本覆蓋mjpegview*/
}
public Bitmap getBitmap() {
return mjpegBitmap;
}
/**
* 計算圖像尺寸
*
* @param bmw bitmap寬
* @param bmh bitmap高
* @return 圖像矩陣
*/
private Rect destRect(int bmw, int bmh) {
int tempx;
int tempy;
/**
* 顯示模式只會在全屏和半屏模式之間切換蔫敲,根本不會進(jìn)入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);/*寬是手機(jī)屏幕的一半*/
if (bmh > dispHeight) {
bmh = dispHeight;
bmw = (int) (dispHeight * bmasp);
}
tempx = (dispWidth / 2) - (bmw / 2);
tempy = (dispHeight / 2) - (bmh / 2);
/**
* Rect(左邊,頂邊吞加,右邊裙犹,下邊)尽狠,功能是繪制一個特定坐標(biāo)的矩形
* 簡單說就是左上角坐標(biāo)為(0,0),右下角坐標(biāo)為(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;
}
/**
* 當(dāng)mjpegview發(fā)生任何結(jié)構(gòu)性的改變時掺冠,將激發(fā)此方法沉馆,前面也提到,渲染線程使用的各種變量需做同步處理
* synchronized內(nèi)的就是同步代碼塊德崭,為了防止線程之間對臨界資源的競爭
*
* @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字節(jié)接癌,根據(jù)上面獲得的寬和高返回一個位圖
*/
Bitmap bm = Bitmap.createBitmap(nWidth, nHeight,
Bitmap.Config.ARGB_8888);
/**
* Canvas :畫布心赶,這是圖像處理的基本單元
* 畫圖時,需要4個重要的元素:
* 1.操作像素的位圖
* 2.繪圖到位圖的畫布
* 3.矩形
* 4. 描述顏色和繪制風(fēng)格的畫筆
* Canvas(bm):構(gòu)造出一個要繪制到位圖的畫布
*/
Canvas c = new Canvas(bm);
/**
* Paint類介紹
* Paint即畫筆缺猛,在繪圖過程中起到了極其重要的作用缨叫,畫筆主要保存了顏色,
* 樣式等繪制信息荔燎,指定了如何繪制文本和圖形耻姥,畫筆對象有很多設(shè)置方法,
* 大體上可以分為兩類有咨,一類與圖形繪制相關(guān)琐簇,一類與文本繪制相關(guān)。
*
* 1.圖形繪制
* setColor(int color);
* 設(shè)置繪制的顏色座享,使用顏色值來表示婉商,該顏色值包括透明度和RGB顏色。
* setDither(boolean dither);
* setXfermode(Xfermode xfermode);
* 設(shè)置圖形重疊時的處理方式渣叛,如合并丈秩,取交集或并集,經(jīng)常用來制作橡皮的擦除效果
*
* 2.文本繪制
* setFakeBoldText(boolean fakeBoldText);
* 模擬實現(xiàn)粗體文字淳衙,設(shè)置在小字體上效果會非常差
* setSubpixelText(boolean subpixelText);
* 設(shè)置該項為true蘑秽,將有助于文本在LCD屏幕上的顯示效果
*
* setTextAlign(Paint.Align align);
* 設(shè)置繪制文字的對齊方向
* setTextSize(float textSize);
* 設(shè)置繪制文字的字號大小
* setTypeface(Typeface typeface);
* 設(shè)置Typeface對象,即字體風(fēng)格箫攀,包括粗體肠牲,斜體以及襯線體,非襯線體等
*/
p.setColor(overlayBackgroundColor);// 背景顏色
c.drawRect(0, 0, nWidth, nHeight, p);/*繪制矩形*/
p.setColor(overlayTextColor);// 文字顏色
/**
* 畫布的繪制文字方法
* test:要繪制的字符
* -b.left:字符起始位置的x坐標(biāo)靴跛,這里是矩形的左邊
* (nHeight / 2) - ((p.ascent() + p.descent()) / 2) + 1:字符起始位置的y坐標(biāo)
* p:用到的畫筆
* 關(guān)于涉及的矩形屬性可看博客 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也創(chuàng)建了的
* 則鎖定畫布com/mjpeg/io/MjpegInputStream.java中的readMjpegFrame方法獲得mjpeg視頻流的內(nèi)容
* mjpeg視頻的內(nèi)容就是類位圖,然后根據(jù)類位圖繪制矩形梢睛,再繪制相應(yīng)的位圖俏险,這個位圖才是我們需要的
* 如果設(shè)置了幀率文本严拒,就在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();/*調(diào)用Inputstrean的方法*/
if (mjpegBitmap == null) {
Log.v("mjpegBitmap","mjpegBitmap is null");
continue;
}
/*同步圖像的寬高設(shè)置*/
synchronized (mSurfaceHolder) {
destRect = destRect(mjpegBitmap.getWidth(),
mjpegBitmap.getHeight());
}
/**
* 當(dāng)主activity點擊相冊和設(shè)置跳轉(zhuǎn)時竖独,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);//線程休眠,讓出調(diào)度
} 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);/* 設(shè)置兩個畫面相交時的模式*/
if (overlayBitmap != null) {
/**
* 計算好文本的寬和高
* 然后調(diào)用畫布的繪制位圖方法繪圖
*/
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表示系統(tǒng)從January 1, 1970 00:00:00.0 UTC開始的毫秒數(shù)
* start在前面已經(jīng)設(shè)置好竞膳,表示渲染線程開始的系統(tǒng)時間
*/
if ((System.currentTimeMillis() - start) >= 1000) {
fps = frameCounter + "fps";
start = System.currentTimeMillis();
overlayBitmap = makeFpsOverlay(overlayPaint, fps);/*真正的繪制這個"文本"*/
frameCounter = 0;
}
}
}
}