安卓干货——PC 连接手机实现摄像头预览

本文详细介绍了如何实现安卓设备开启摄像头预览,并作为服务器端,允许PC客户端通过socket通信连接,实现实时预览及远程拍照功能。通过Android权限配置、SurfaceView、相机操作、socket通信和EventBus框架,实现手机与PC之间的数据交互。
摘要由CSDN通过智能技术生成

1 功能需求

(1)手机端打开摄像头实时预览;
(2)手机端作为服务端,PC端作为客户端连接;
(3)连接成功后PC端可以同时预览手机端的摄像头采集的图像;
(4)PC端点击拍照可以控制手机端拍摄一张照片,并将照片传给PC端。

2 功能模块

(1)安卓手机打开摄像头并实现预览和拍照功能;
(2)手机端开启监听,并在连接成功后将摄像头采集的数据传给PC;
(3)手机端读取PC发送的命令指令,执行相应的操作。

3 开发流程

3.1 开启摄像头实现预览
(1) 获取摄像头权限,并添加自动对焦属性。
  在应用程序的manifest.xml文件中添加使用手机摄像头权限。由于程序需要使用socket的通信过程,将手机作为服务端,因此需要添加网络权限。

<uses-permission android:name="android.permission.CAMERA" />
 <uses-feature android:name="android.hardware.camera" />  
 <uses-feature android:name="android.hardware.camera.autofocus" />
 <uses-permission Android:name="android.permission.INTERNET"/>

2)实现相机的实时预览。
   安卓系统使用SurfaceView即可完成预览功能。在布局文件中添加SurfaceView。activity_main.xml布局文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/gray_light"
android:orientation="vertical" >

<SurfaceView
    android:id="@+id/surview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:scaleType="fitCenter" />

</LinearLayout>

(3)在MainActivity 的onCreate中初始化SurfceView。初始化方法如下:

private void initSurfaceView() {
   
    surfaceView = (SurfaceView) findViewById(R.id.surview);
    surfaceHolder = surfaceView.getHolder();
    surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    surfaceHolder.setKeepScreenOn(true);
    surfaceHolder.addCallback(new Callback() {
   

        @Override
        public void surfaceDestroyed(SurfaceHolder arg0) {
   

        }

        @Override
        public void surfaceCreated(SurfaceHolder arg0) {
   
            // 开启摄像头
            startCamera(curCameraIndex);
        }

        @Override
        public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2,
                int arg3) {
   

        }
    });
    surfaceView.setFocusable(true);
    surfaceView.setBackgroundColor(TRIM_MEMORY_BACKGROUND);

}

4)SurfceView创建完成时调用开启摄像头的方法startCamera()。startCamera方法如下,其中代码含义已在注释程序中注明。

// 根据索引初始化摄像头
@SuppressLint("NewApi")
public void startCamera(int cameraIndex) {
   

    // 先停止摄像头
    stopCamera();
    // 再初始化并打开摄像头
    if (camera == null) {
   
        //打开手机摄像头,cameraIndex为摄像头索引,0代表后置摄像头,1代表前置摄像头。
        camera = Camera.open(cameraIndex);
        //创建摄像头操作工具类
        cameraUtil = new CameraUtil(camera, callback);
        //开启预览
        cameraUtil.initCamera(srcFrameHeight, srcFrameWidth, surfaceHolder);
        Log.e(TAG, "打开相机");
    }
}

// 停止并释放摄像头
public void stopCamera() {
   
    if (camera != null) {
   
        camera.setPreviewCallback(null);
        camera.stopPreview();
        camera.release();
        camera = null;
    }
}

//摄像头开启预览后采集到的数据回调接口
PreviewCallback callback = new PreviewCallback() {
   

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
   
        Size size = camera.getParameters().getPreviewSize();
        try {
   
            if (times == 0) {
   
                YuvImage image = new YuvImage(data, ImageFormat.NV21,
                        size.width, size.height, null);
                if (image != null) {
   
                    // 将YuvImage对象转为字节数组
                    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                    image.compressToJpeg(new Rect(0, 0, size.width,
                            size.height), 100, outputStream);
                    byte[] srcData = outputStream.toByteArray();
                    int len = srcData.length;

                    // 字节数组转为Bitmap
                    Bitmap src = BitmapFactory.decodeByteArray(srcData, 0,
                            len);
                    src = BitmapUtil.rotate(src, 90);
                    // 压缩Bitmap,并获取压缩后的字节数组,即可获取预览数据文件
                    // outdata数据即是待发送给PC端的数据
                    byte[] outdata = BitmapUtil.transImage(src,
                            srcFrameWidth / 4, srcFrameHeight / 4);
                    int datalen = outdata.length;

                    if (isOpen) {
   
                        // 写入头
                        sendData(new byte[] {
    (byte) 0xA0 });
                        // 写入数组长度
                        sendData(intToByteArray(datalen));
                        // 写入数据值
                        sendData(outdata);
                    }

                    // 回收Bitmap
                    if (!src.isRecycled()) {
   
                        src.recycle();
                    }
                }
            }
        } catch (Exception e) {
   
            e.printStackTrace();
        }
    }
};

(5)CameraUtil为设置摄像头的辅助类,代码如下:

public class CameraUtil {
   
    //摄像头类
    Camera camera;
    int cameraIndex;
    //预览视图宽和高
    int srcFrameWidth;
    int srcFrameHeight;
    SurfaceHolder surfaceHolder;
    PreviewCallback callback;

    public CameraUtil(Camera camera, PreviewCallback callback) {
   
        this.camera = camera;
        this.callback = callback;
       }

    //摄像头预览初始化
    public void initCamera(final int srcFrameWidth, final int srcFrameHeight, final SurfaceHolder surfaceHolder) {
   

        this.srcFrameHeight = srcFrameHeight;
        this.srcFrameWidth = srcFrameWidth;
        this.surfaceHolder = surfaceHolder;
        Camera.Parameters params = camera.getParameters();
        //params.setPreviewSize(srcFrameWidth / 4, srcFrameHeight / 4);
        //设置预览格式
        params.setPreviewFormat(ImageFormat.NV21);
        params.setPreviewFrameRate(30);
        //设置图像质量
        params.setJpegQuality(100);
        params.setPictureFormat(ImageFormat.JPEG);
        //设置预览方向
        params.set("orientation", "portrait");
        params.set("rotation", 90);
        //设置对焦模式为自动连续对焦
        params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);// 1连续对焦
        camera.setParameters(params);
        camera.setDisplayOrientation(90);
        // 设置显示图像的SurfaceView
        try {
   
            camera.setPreviewDisplay<
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值