把Android手机变成远程监控摄像头

要买新手机了旧手机怎么办?我们可以废物利用下,把旧的手机变成一个远程监控摄像头。这里使用Java创建手机camera客户端和远程服务上的监控界面。

实现方法考虑几点:

  1. 创建一个Android自定义的摄像头应用

  2. 把preview的数据发送到服务端

  3. preview的NV21数据解码

  4. 把图像画出来

Android摄像头,Socket链接,服务端图像显示

创建preview回调函数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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);
             }
         }
     };

注册回调函数:

?
1
2
3
4
5
6
7
8
try  {
             mCamera.setPreviewCallback(mPreviewCallback);
             mCamera.setPreviewDisplay(mHolder);
             mCamera.startPreview();
  
         catch  (Exception e){
             Log.d(TAG,  "Error starting camera preview: "  + e.getMessage());
         }

停止释放摄像头:

?
1
2
3
4
5
6
7
public  void  onPause() {
         if  (mCamera !=  null ) {
             mCamera.setPreviewCallback( null );
             mCamera.stopPreview();
         }
         resetBuff();
     }

使用AlertDialog来设置服务器IP地址:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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数据和每一帧的图像数据:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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();

服务端接收数据:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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解码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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

?
1
2
3
4
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);
 
?
1
2
3
4
5
6
7
8
9
10
11
12
        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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值