要买新手机了旧手机怎么办?我们可以废物利用下,把旧的手机变成一个远程监控摄像头。这里使用Java创建手机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<byte[]> 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); } }
源码
1">https://github.com/DynamsoftRD/Android-IP-Camera
时间: 2024-11-05 13:49:33