2019独角兽企业重金招聘Python工程师标准>>>
要买新手机了旧手机怎么办?我们可以废物利用下,把旧的手机变成一个远程监控摄像头。这里使用Java创建手机camera客户端和远程服务上的监控界面。
参考原文:
Making Android Smart Phone a Remote IP Camera
实现方法考虑几点:
创建一个Android自定义的摄像头应用
把preview的数据发送到服务端
preview的NV21数据解码
把图像画出来
Android摄像头,Socket链接,服务端图像显示
创建preview回调函数:
private Camera.PreviewCallback mPreviewCallback = new PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
// TODO Auto-generated method stub
synchronized (mQueue) {
if (mQueue.size() == MAX_BUFFER) {
mQueue.poll();
}
mQueue.add(data);
}
}
};
注册回调函数:
try {
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
停止释放摄像头:
public void onPause() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
}
resetBuff();
}
使用AlertDialog来设置服务器IP地址:
private void setting() {
LayoutInflater factory = LayoutInflater.from(this);
final View textEntryView = factory.inflate(R.layout.server_setting, null);
AlertDialog dialog = new AlertDialog.Builder(IPCamera.this)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(R.string.setting_title)
.setView(textEntryView)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
EditText ipEdit = (EditText)textEntryView.findViewById(R.id.ip_edit);
EditText portEdit = (EditText)textEntryView.findViewById(R.id.port_edit);
mIP = ipEdit.getText().toString();
mPort = Integer.parseInt(portEdit.getText().toString());
Toast.makeText(IPCamera.this, "New address: " + mIP + ":" + mPort, Toast.LENGTH_LONG).show();
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
/* User clicked cancel so do some stuff */
}
})
.create();
dialog.show();
}
启动线程来创建socket链接,发送JSON数据和每一帧的图像数据:
mSocket = new Socket();
mSocket.connect(new InetSocketAddress(mIP, mPort), 10000);
BufferedOutputStream outputStream = new BufferedOutputStream(mSocket.getOutputStream());
BufferedInputStream inputStream = new BufferedInputStream(mSocket.getInputStream());
JsonObject jsonObj = new JsonObject();
jsonObj.addProperty("type", "data");
jsonObj.addProperty("length", mCameraPreview.getPreviewLength());
jsonObj.addProperty("width", mCameraPreview.getPreviewWidth());
jsonObj.addProperty("height", mCameraPreview.getPreviewHeight());
byte[] buff = new byte[256];
int len = 0;
String msg = null;
outputStream.write(jsonObj.toString().getBytes());
outputStream.flush();
while ((len = inputStream.read(buff)) != -1) {
msg = new String(buff, 0, len);
// JSON analysis
JsonParser parser = new JsonParser();
boolean isJSON = true;
JsonElement element = null;
try {
element = parser.parse(msg);
}
catch (JsonParseException e) {
Log.e(TAG, "exception: " + e);
isJSON = false;
}
if (isJSON && element != null) {
JsonObject obj = element.getAsJsonObject();
element = obj.get("state");
if (element != null && element.getAsString().equals("ok")) {
// send data
while (true) {
outputStream.write(mCameraPreview.getImageBuffer());
outputStream.flush();
if (Thread.currentThread().isInterrupted())
break;
}
break;
}
}
else {
break;
}
}
outputStream.close();
inputStream.close();
服务端接收数据:
public int fillBuffer(byte[] data, int off, int len, LinkedList YUVQueue) {
mTotalLength += len;
mByteArrayOutputStream.write(data, off, len);
if (mTotalLength == mFrameLength) {
synchronized (YUVQueue) {
YUVQueue.add(mByteArrayOutputStream.toByteArray());
mByteArrayOutputStream.reset();
}
mTotalLength = 0;
System.out.println("received file");
}
return 0;
}
NV21解码:
public static int[] convertYUVtoRGB(byte[] yuv, int width, int height)
throws NullPointerException, IllegalArgumentException {
int[] out = new int[width * height];
int sz = width * height;
int i, j;
int Y, Cr = 0, Cb = 0;
for (j = 0; j < height; j++) {
int pixPtr = j * width;
final int jDiv2 = j >> 1;
for (i = 0; i < width; i++) {
Y = yuv[pixPtr];
if (Y < 0)
Y += 255;
if ((i & 0x1) != 1) {
final int cOff = sz + jDiv2 * width + (i >> 1) * 2;
Cb = yuv[cOff];
if (Cb < 0)
Cb += 127;
else
Cb -= 128;
Cr = yuv[cOff + 1];
if (Cr < 0)
Cr += 127;
else
Cr -= 128;
}
int R = Y + Cr + (Cr >> 2) + (Cr >> 3) + (Cr >> 5);
if (R < 0)
R = 0;
else if (R > 255)
R = 255;
int G = Y - (Cb >> 2) + (Cb >> 4) + (Cb >> 5) - (Cr >> 1)
+ (Cr >> 3) + (Cr >> 4) + (Cr >> 5);
if (G < 0)
G = 0;
else if (G > 255)
G = 255;
int B = Y + Cb + (Cb >> 1) + (Cb >> 2) + (Cb >> 6);
if (B < 0)
B = 0;
else if (B > 255)
B = 255;
out[pixPtr++] = 0xff000000 + (B << 16) + (G << 8) + R;
}
}
return out;
}
使用Swing绘制BufferedImage:
BufferedImage bufferedImage = null;
int[] rgbArray = Utils.convertYUVtoRGB(data, mWidth, mHeight);
bufferedImage = new BufferedImage(mWidth, mHeight, BufferedImage.TYPE_USHORT_565_RGB);
bufferedImage.setRGB(0, 0, mWidth, mHeight, rgbArray, 0, mWidth);
synchronized (mQueue) {
if (mQueue.size() > 0) {
mLastFrame = mQueue.poll();
}
}
if (mLastFrame != null) {
g.drawImage(mLastFrame, 0, 0, null);
}
else if (mImage != null) {
g.drawImage(mImage, 0, 0, null);
}
}
源码
https://github.com/DynamsoftRD/Android-IP-Camera