我想用Monodroid构建一个应用程序,将IPCamera(MJpeg格式)的实时视频流传输到我的平板电脑.在挖掘互联网后,我发现有一个从
here开始用Java编写的Mjpeg Library项目.它有两个文件MjpegView.java和MjpegInputStream.Java,我把这两个文件放在这里:
MjpegView.java
package de.mjpegsample.MjpegView; 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.view.SurfaceHolder; import android.view.SurfaceView; public class MjpegView extends SurfaceView implements SurfaceHolder.Callback { 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 SIZE_STANDARD = 1; public final static int SIZE_BEST_FIT = 4; public final static int SIZE_FULLSCREEN = 8; private MjpegViewThread thread; private MjpegInputStream mIn = null; private boolean showFps = false; private boolean mRun = false; private boolean surfaceDone = false; private Paint overlayPaint; private int overlayTextColor; private int overlayBackgroundColor; private int ovlPos; private int dispWidth; private int dispHeight; private int displayMode; public class MjpegViewThread extends Thread { private SurfaceHolder mSurfaceHolder; private int frameCounter = 0; private long start; private Bitmap ovl; public MjpegViewThread(SurfaceHolder surfaceHolder,Context context) { mSurfaceHolder = surfaceHolder; } private Rect destRect(int bmw,int bmh) { int tempx; int tempy; if (displayMode == MjpegView.SIZE_STANDARD) { tempx = (dispWidth / 2) - (bmw / 2); tempy = (dispHeight / 2) - (bmh / 2); return new Rect(tempx,tempy,bmw + tempx,bmh + tempy); } if (displayMode == MjpegView.SIZE_BEST_FIT) { 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); return new Rect(tempx,bmh + tempy); } if (displayMode == MjpegView.SIZE_FULLSCREEN) return new Rect(0,dispWidth,dispHeight); return null; } public void setSurfaceSize(int width,int height) { synchronized(mSurfaceHolder) { dispWidth = width; dispHeight = height; } } private Bitmap makeFpsOverlay(Paint p,String text) { Rect b = new Rect(); p.getTextBounds(text,text.length(),b); int bwidth = b.width()+2; int bheight = b.height()+2; Bitmap bm = Bitmap.createBitmap(bwidth,bheight,Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); p.setColor(overlayBackgroundColor); c.drawRect(0,bwidth,p); p.setColor(overlayTextColor); c.drawText(text,-b.left+1,(bheight/2)-((p.ascent()+p.descent())/2)+1,p); return bm; } public void run() { start = System.currentTimeMillis(); PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.DST_OVER); Bitmap bm; int width; int height; Rect destRect; Canvas c = null; Paint p = new Paint(); String fps = ""; while (mRun) { if(surfaceDone) { try { c = mSurfaceHolder.lockCanvas(); synchronized (mSurfaceHolder) { try { bm = mIn.readMjpegFrame(); destRect = destRect(bm.getWidth(),bm.getHeight()); c.drawColor(Color.BLACK); c.drawBitmap(bm,null,destRect,p); if(showFps) { p.setXfermode(mode); if(ovl != null) { height = ((ovlPos & 1) == 1) ? destRect.top : destRect.bottom-ovl.getHeight(); width = ((ovlPos & 8) == 8) ? destRect.left : destRect.right -ovl.getWidth(); c.drawBitmap(ovl,width,height,null); } p.setXfermode(null); frameCounter++; if((System.currentTimeMillis() - start) >= 1000) { fps = String.valueOf(frameCounter)+"fps"; frameCounter = 0; start = System.currentTimeMillis(); ovl = makeFpsOverlay(overlayPaint,fps); } } } catch (IOException e) {} } } finally { if (c != null) mSurfaceHolder.unlockCanvasAndPost(c); } } } } } private void init(Context context) { SurfaceHolder holder = getHolder(); holder.addCallback(this); thread = new MjpegViewThread(holder,context); setFocusable(true); overlayPaint = new Paint(); overlayPaint.setTextAlign(Paint.Align.LEFT); overlayPaint.setTextSize(12); overlayPaint.setTypeface(Typeface.DEFAULT); overlayTextColor = Color.WHITE; overlayBackgroundColor = Color.BLACK; ovlPos = MjpegView.POSITION_LOWER_RIGHT; displayMode = MjpegView.SIZE_STANDARD; dispWidth = getWidth(); dispHeight = getHeight(); } public void startPlayback() { if(mIn != null) { mRun = true; thread.start(); } } public void stopPlayback() { mRun = false; boolean retry = true; while(retry) { try { thread.join(); retry = false; } catch (InterruptedException e) {} } } public MjpegView(Context context,AttributeSet attrs) { super(context,attrs); init(context); } public void surfaceChanged(SurfaceHolder holder,int f,int w,int h) { thread.setSurfaceSize(w,h); } public void surfaceDestroyed(SurfaceHolder holder) { surfaceDone = false; stopPlayback(); } public MjpegView(Context context) { super(context); init(context); } public void surfaceCreated(SurfaceHolder holder) { surfaceDone = true; } public void showFps(boolean b) { showFps = b; } public void setSource(MjpegInputStream source) { mIn = source; startPlayback();} public void setOverlayPaint(Paint p) { overlayPaint = p; } public void setOverlayTextColor(int c) { overlayTextColor = c; } public void setOverlayBackgroundColor(int c) { overlayBackgroundColor = c; } public void setOverlayPosition(int p) { ovlPos = p; } public void setDisplayMode(int s) { displayMode = s; } }
MjpegInputStream.Java
package de.mjpegsample.MjpegView; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Properties; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public class MjpegInputStream extends DataInputStream { 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; public static MjpegInputStream read(String url) { HttpResponse res; DefaultHttpClient httpclient = new DefaultHttpClient(); try { res = httpclient.execute(new HttpGet(URI.create(url))); return new MjpegInputStream(res.getEntity().getContent()); } catch (ClientProtocolException e) { } catch (IOException e) {} return null; } public MjpegInputStream(InputStream in) { super(new BufferedInputStream(in,FRAME_MAX_LENGTH)); } private int getEndOfSeqeunce(DataInputStream in,byte[] sequence) throws IOException { int seqIndex = 0; byte c; for(int i=0; i < FRAME_MAX_LENGTH; i++) { c = (byte) in.readUnsignedByte(); if(c == sequence[seqIndex]) { seqIndex++; if(seqIndex == sequence.length) return i + 1; } else seqIndex = 0; } return -1; } private int getStartOfSequence(DataInputStream in,byte[] sequence) throws IOException { int end = getEndOfSeqeunce(in,sequence); return (end < 0) ? (-1) : (end - sequence.length); } private int parseContentLength(byte[] headerBytes) throws IOException,NumberFormatException { ByteArrayInputStream headerIn = new ByteArrayInputStream(headerBytes); Properties props = new Properties(); props.load(headerIn); return Integer.parseInt(props.getProperty(CONTENT_LENGTH)); } public Bitmap readMjpegFrame() throws IOException { mark(FRAME_MAX_LENGTH); int headerLen = getStartOfSequence(this,SOI_MARKER); reset(); byte[] header = new byte[headerLen]; readFully(header); try { mContentLength = parseContentLength(header); } catch (NumberFormatException nfe) { mContentLength = getEndOfSeqeunce(this,EOF_MARKER); } reset(); byte[] frameData = new byte[mContentLength]; skipBytes(headerLen); readFully(frameData); return BitmapFactory.decodeStream(new ByteArrayInputStream(frameData)); } }
所以我转换了(实际上创建一个c#包装)与Binding Library项目.
但是,尽管我遵循了这个项目的示例代码教程如下:
样本本身:
public class MjpegSample extends Activity { private MjpegView mv; public void onCreate(Bundle icicle) { super.onCreate(icicle); //sample public cam String URL = "http://webcam5.hrz.tu-darmstadt.de/axis-cgi/mjpg/video.cgi?resolution=320x240"; requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); mv = new MjpegView(this); setContentView(mv); mv.setSource(MjpegInputStream.read(URL)); mv.setDisplayMode(MjpegView.SIZE_BEST_FIT); mv.showFps(true); }
我在Monodroid中做了什么
namespace AndroidApplication8 { [Activity(Label = "AndroidApplication8",MainLauncher = true,Icon = "@drawable/icon")] public class Activity1 : Activity { int count = 1; protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); String URL = "rtsp://192.168.1.3/Mjpeg/video.cgi"; var mv = new MjpegView(this); SetContentView(mv); **mv.SetSource(MjpegInputStream.Read(URL)); mv.SetDisplayMode(MjpegView.SizeBestFit); mv.StartPlayback(); } } }
但是当它想要执行MjpegInputStream.Read()时,它给出了**中指示的行中的错误.
解决方法
你应该检查你的视频类型.例如,如果你的视频编码压缩在那里(在进入你的Android设备之前),你应该把它编入它的浏览器之前.这可以让你在java中编写一个代码来验证从摄像头传入流(不要使用Android的内置浏览器),然后手动解码. 祝你好运!