Android 单摄像头二摄像头预览

Android Camera two preview from single camera

我想构建应用程序 android 关于从单个相机预览两个相机 enter image description here

我尝试构建应用程序,我在 Android Studio 中使用 SurfaceView 但我有问题,enter image description here

我想要示例代码,你能帮帮我吗? 非常感谢。


我试过应用程序。

AndroidSurfaceviewExample.java

package th.in.cybernoi.cardboardview;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;


public class AndroidSurfaceviewExample extends Activity implements SurfaceHolder.Callback  {
    TextView testView;

    Camera camera;
    SurfaceView surfaceView;
    SurfaceHolder surfaceHolder;

    PictureCallback rawCallback;
    ShutterCallback shutterCallback;
    PictureCallback jpegCallback;


    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        surfaceHolder.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        jpegCallback = new PictureCallback() {
            public void onPictureTaken(byte[] data, Camera camera) {
                FileOutputStream outStream = null;
                try {
                    outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
                    outStream.write(data);
                    outStream.close();
                    Log.d("Log", "onPictureTaken - wrote bytes: " + data.length);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                }
                Toast.makeText(getApplicationContext(), "Picture Saved", 2000).show();
                refreshCamera();
            }
        };
    }



    //Surfacrview
    public void captureImage(View v) throws IOException {
        //take the picture
        camera.takePicture(null, null, jpegCallback);
    }

    public void refreshCamera() {
        if (surfaceHolder.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            camera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        // start preview with new settings
        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        } catch (Exception e) {

        }
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        refreshCamera();
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // open the camera
            camera = Camera.open();
        } catch (RuntimeException e) {
            // check for exceptions
            System.err.println(e);
            return;
        }
        Camera.Parameters param;
        param = camera.getParameters();

        // modify parameter
        param.setPreviewSize(352, 288);
        camera.setParameters(param);
        try {
            // The Surface has been created, now tell the camera where to draw
            // the preview.
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        } catch (Exception e) {
            // check for exceptions
            System.err.println(e);
            return;
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // stop preview and release camera
        camera.stopPreview();
        camera.release();
        camera = null;
    }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<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:orientation="horizontal"
    tools:context="th.in.cybernoi.cardboardview.MainActivity">

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

</LinearLayout>


立即更新编辑代码:16 年 9 月 2 日

  1. 编辑为AndroidSurfaceviewExample.java
    • 添加public void onPreviewFrame()

    public class AndroidSurfaceviewExample extends Activity implements SurfaceHolder.Callback {
    TextView testView;

    Camera camera;
    SurfaceHolder camera01;
    SurfaceHolder camera02;

    SurfaceView surfaceViewLeft;
    SurfaceHolder surfaceHolderLeft;

    SurfaceView surfaceViewRight;
    SurfaceHolder surfaceHolderRight;

    PictureCallback rawCallback;
    ShutterCallback shutterCallback;
    PictureCallback jpegCallback;
    /**
     * ATTENTION: This was auto-generated to implement the App Indexing API.
     * See https://g.co/AppIndexing/AndroidStudio for more information.
     */
    private GoogleApiClient client;


    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        surfaceViewLeft = (SurfaceView) findViewById(R.id.surfaceViewLeft);
        surfaceHolderLeft = surfaceViewLeft.getHolder();

        surfaceViewRight = (SurfaceView) findViewById(R.id.surfaceViewRight);
        surfaceHolderRight = surfaceViewRight.getHolder();

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        surfaceHolderLeft.addCallback(this);
        surfaceHolderRight.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        surfaceHolderLeft.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        surfaceHolderRight.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        /*
        jpegCallback = new PictureCallback() {
            public void onPictureTaken(byte[] data, Camera camera) {
                FileOutputStream outStream = null;
                try {
                    outStream = new FileOutputStream(String.format("/sdcard/%d.jpg", System.currentTimeMillis()));
                    outStream.write(data);
                    outStream.close();
                    Log.d("Log", "onPictureTaken - wrote bytes: " + data.length);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                }
                Toast.makeText(getApplicationContext(), "Picture Saved", 2000).show();
                refreshCamera();
            }


        };
        */
        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
    }


    //Surfacrview


    public void refreshCamera() {
        if (surfaceHolderLeft.getSurface() == null) {
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        try {
            camera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        // start preview with new settings
        try {
            camera.setPreviewDisplay(surfaceHolderLeft);
            camera.startPreview();


        } catch (Exception e) {

        }
    }



    public void onPreviewFrame() {

        if (surfaceHolderRight.getSurface() == null) {
            // preview surface does not exist
            return;
        }


        // stop preview before making changes
        try {
            camera.stopPreview();
        } catch (Exception e) {
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        // start preview with new settings
        try {
            surfaceHolderLeft.lockCanvas();
            camera.setPreviewDisplay(surfaceHolderRight);
            camera.setPreviewCallback((Camera.PreviewCallback) surfaceHolderLeft);
            camera.startPreview();


        } catch (Exception e) {

        }


    }


    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // Now that the size is known, set up the camera parameters and begin
        // the preview.
        refreshCamera();
        onPreviewFrame();
        
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // open the camera
            camera = Camera.open();
        } catch (RuntimeException e) {
            // check for exceptions
            System.err.println(e);
            return;
        }
        Camera.Parameters param;
        param = camera.getParameters();

        // modify parameter
        param.setPreviewSize(352, 288);
        camera.setParameters(param);
        try {
            // The Surface has been created, now tell the camera where to draw
            // the preview.
            camera.setPreviewDisplay(surfaceHolderLeft);
            camera.startPreview();
        } catch (Exception e) {
            // check for exceptions
            System.err.println(e);
            return;
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        // stop preview and release camera
        camera.stopPreview();
        camera.release();
        camera = null;
    }

    @Override
    public void onStart() {
        super.onStart();

        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        client.connect();
        Action viewAction = Action.newAction(
                Action.TYPE_VIEW, // TODO: choose an action type.
                "AndroidSurfaceviewExample Page", // TODO: Define a title for the content shown.
                // TODO: If you have web page content that matches this app activity's content,
                // make sure this auto-generated web page URL is correct.
                // Otherwise, set the URL to null.
                Uri.parse("http://host/path"),
                // TODO: Make sure this auto-generated app deep link URI is correct.
                Uri.parse("android-app://th.in.cybernoi.cardboardview/http/host/path")
        );
        AppIndex.AppIndexApi.start(client, viewAction);
    }

    @Override
    public void onStop() {
        super.onStop();

        // ATTENTION: This was auto-generated to implement the App Indexing API.
        // See https://g.co/AppIndexing/AndroidStudio for more information.
        Action viewAction = Action.newAction(
                Action.TYPE_VIEW, // TODO: choose an action type.
                "AndroidSurfaceviewExample Page", // TODO: Define a title for the content shown.
                // TODO: If you have web page content that matches this app activity's content,
                // make sure this auto-generated web page URL is correct.
                // Otherwise, set the URL to null.
                Uri.parse("http://host/path"),
                // TODO: Make sure this auto-generated app deep link URI is correct.
                Uri.parse("android-app://th.in.cybernoi.cardboardview/http/host/path")
        );
        AppIndex.AppIndexApi.end(client, viewAction);
        client.disconnect();
    }
}

布局中的每个元素都需要有一个唯一的 ID。您已将两个屏幕元素标识为 @+id/surfaceView。尝试使它们都独一无二。请参阅下面修改后的布局文件 - 也不要忘记更新您的源代码以填充两个屏幕元素。

<?xml version="1.0" encoding="utf-8"?>
<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:orientation="horizontal"
    tools:context="th.in.cybernoi.cardboardview.MainActivity">

    <SurfaceView
        android:id="@+id/surfaceViewLeft"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <SurfaceView
        android:id="@+id/surfaceViewRight"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

</LinearLayout>

将代码添加到您的程序以引用两个 SurfaceView 对象。您只需复制已有的代码即可。您还应该检查程序的其余部分是否有对需要更新的 SurfaceView 对象的其他引用。

            surfaceViewLeft = (SurfaceView) findViewById(R.id.surfaceViewLeft);
        surfaceHolderLeft = surfaceViewLeft.getHolder();

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        surfaceHolderLeft.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        surfaceHolderLeft.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        surfaceViewRight = (SurfaceView) findViewById(R.id.surfaceViewRight);
        surfaceHolderRight = surfaceViewRight.getHolder();

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        surfaceHolderRight.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        surfaceHolderRight.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 

关键是要意识到,虽然你的左视图和右视图是同一类型的对象(它们都是SurfaceViews),但它们完全不知道对方 .如果您希望某个视图发生某些事情,您必须编写明确适用于该视图的代码。如果您还希望它在其他视图中发生,则必须编写可在其他视图中显式工作的代码。这是一个具有相同 对象的两个 实例 的示例。

更新:经过进一步挖掘,我发现上述更改是必要的,但还不够。您对 camera.setPreviewDisplay(surfaceHolderLeft); 的调用仅允许定义单个预览 SurfaceView,因此您无法使用此方法在两个表面上进行预览。

为了完成您正在尝试做的事情,我建议创建预览图像的位图(可以在名为 onPreviewFrame() 的回调中获得,这类似于您的 onPictureTaken() callback. The bitmap data will be passed in through the first parameter) Then get aCanvasfor each Surface view (by callingSurfaceHolder.lockCanvas(), and then draw the bitmap you saved onto each Canvas by callingCanvas.drawBitmap()`。这应该将原始图像绘制到每个表面视图上。

由于您将直接绘制到每个 surfaceView,因此您应该删除将其中一个 surfaceView 设置为预览的行,这样您就可以与相机竞争以获取在那里绘制的权限。摆脱行 camera.setPreviewDisplay(surfaceHolderLeft); 而是写 camera.setPreviewCallback(yourcallbackgoeshere);

祝你好运。

我成功地实现了单相机克隆预览。参考下面的代码片段。

private Camera.PreviewCallback cbPreview = new Camera.PreviewCallback() {
    @Override
    public void onPreviewFrame(byte[] bytes, Camera camera) {
        int pf = mCamera.getParameters().getPreviewFormat();
        if (pf == ImageFormat.NV21)
            bmp = decodeNV21(bytes, camera);
        else
            bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        MainActivity.mImageView.setImageBitmap(bmp);
        MainActivity.mImageView.postInvalidate();
    }
};

private Bitmap decodeNV21(byte[] data, Camera camera){
    Bitmap retimage = null;

    int w = camera.getParameters().getPreviewSize().width;
    int h = camera.getParameters().getPreviewSize().height;

    //Get the YUV image
    YuvImage yuv_image = new YuvImage(data, camera.getParameters().getPreviewFormat(), w, h, null);

    //Convert Yuv to Jpeg
    Rect rect = new Rect(0, 0, w, h);
    ByteArrayOutputStream out_stream = new ByteArrayOutputStream();
    yuv_image.compressToJpeg(rect, 100, out_stream);

    //Convert Yuv to jpeg
    retimage = BitmapFactory.decodeByteArray(out_stream.toByteArray(), 0, out_stream.size());

    return retimage;
}