Предварительный просмотр камеры на нескольких видах - инициализация / обработка выпуска

1

Я инкапсулировал использование камеры Android в один класс (CameraAccess), который использует невидимую SurfaceTexture в качестве предварительного просмотра и реализует Camera.PreviewCallback. Внутри этого обратного вызова я получаю массив байтов текущего кадра, который затем я хочу использовать для нескольких видов/фрагментов.

Моя проблема - управление жизненным циклом. Обычно камера используется в одном представлении и инициализируется/выпускается в onSurfaceCreated и onSurfaceDestroyed (см. SurfaceHolder.Callback). Но в моем сценарии мне нужно использовать предварительный просмотр более чем на одном представлении. Каждое представление добавляет себя в качестве обратного вызова к классу CameraAccess.

Я думал, что CameraAccess будет входить в класс Application. Но когда вы нажимаете кнопку "домой", приложение остается в живых, но все виды уничтожаются. Как бы вы справились с инициализацией и выпуском камеры?

Теги:
camera

1 ответ

1
Лучший ответ

Вот как я это решил.

Я включил всю логику в одноэлементный класс CameraAccess. Все представления, которые хотят получить доступ к камере/превью, будут реализовывать CameraFrameCallback и добавлять/удалять себя в onSurfaceCreated/onSurfaceDestroyed. Всякий раз, когда принимается кадр, он обрабатывается инкапсулированным классом фреймов, который заботится о преобразовании цветового пространства и преобразовании в растровое изображение. Он также отвечает за выделение памяти для растрового изображения, которое затем используется всеми представлениями, когда они рисуют содержимое на их холсте. Как вы увидите, я использовал OpenCV для упрощения преобразования цветового пространства. Матрица OpenCV (класс Matrix) также хороша для последующей обработки изображений.

public class CameraAccess implements Camera.PreviewCallback,
        LoaderCallbackInterface {

    // see http://developer.android.com/guide/topics/media/camera.html for more
    // details

    final static String TAG = "CameraAccess";
    Context context;
    int cameraIndex; // example: CameraInfo.CAMERA_FACING_FRONT or
    // CameraInfo.CAMERA_FACING_BACK
    Camera mCamera;
    int mFrameWidth;
    int mFrameHeight;
    Mat mFrame;
    CameraAccessFrame mCameraFrame;
    List<CameraFrameCallback> mCallbacks = new ArrayList<CameraFrameCallback>();
    boolean mOpenCVloaded;
    byte mBuffer[]; // needed to avoid OpenCV error:
                    // "queueBuffer: BufferQueue has been abandoned!"

    private static CameraAccess mInstance;

    public static CameraAccess getInstance(Context context, int cameraIndex) {
        if (mInstance != null)
            return mInstance;

        mInstance = new CameraAccess(context, cameraIndex);
        return mInstance;
    }

    private CameraAccess(Context context, int cameraIndex) {
        this.context = context;
        this.cameraIndex = cameraIndex;

        if (!OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_7, context,
                this)) {
            Log.e(TAG, "Cannot connect to OpenCVManager");
        } else
            Log.d(TAG, "OpenCVManager successfully connected");
    }

    private boolean checkCameraHardware() {
        if (context.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_CAMERA)) {
            // this device has a camera
            return true;
        } else {
            // no camera on this device
            return false;
        }
    }

    public static Camera getCameraInstance(int cameraIndex) {
        Camera c = null;
        try {
            c = Camera.open(cameraIndex); // attempt to get a
                                            // Camera
                                            // instance

            Log.d(TAG, "Camera opened. index: " + cameraIndex);
        } catch (Exception e) {
            // Camera is not available (in use or does not exist)
        }
        return c; // returns null if camera is unavailable
    }

    public void addCallback(CameraFrameCallback callback) {
        // we don't care if the callback is already in the list
        this.mCallbacks.add(callback);

        if (mCamera != null)
            callback.onCameraInitialized(mFrameWidth, mFrameHeight);
        else if (mOpenCVloaded)
            connectCamera();
    }

    public void removeCallback(CameraFrameCallback callback) {
        boolean removed = false;
        do {
            // someone might have added the callback multiple times
            removed = this.mCallbacks.remove(callback);

            if (removed)
                callback.onCameraReleased();

        } while (removed == true);

        if (mCallbacks.size() == 0)
            releaseCamera();
    }

    @Override
    public void onPreviewFrame(byte[] frame, Camera arg1) {
        mFrame.put(0, 0, frame);
        mCameraFrame.invalidate();

        for (CameraFrameCallback callback : mCallbacks)
            callback.onFrameReceived(mCameraFrame);

        if (mCamera != null)
            mCamera.addCallbackBuffer(mBuffer);
    }

    private void connectCamera() {
        synchronized (this) {
            if (true) {// checkCameraHardware()) {
                mCamera = getCameraInstance(cameraIndex);

                Parameters params = mCamera.getParameters();
                List<Camera.Size> sizes = params.getSupportedPreviewSizes();

                // Camera.Size previewSize = sizes.get(0);
                Collections.sort(sizes, new PreviewSizeComparer());
                Camera.Size previewSize = null;
                for (Camera.Size s : sizes) {
                    if (s == null)
                        break;

                    previewSize = s;
                }

                // List<Integer> formats = params.getSupportedPictureFormats();
                // params.setPreviewFormat(ImageFormat.NV21);

                params.setPreviewSize(previewSize.width, previewSize.height);
                mCamera.setParameters(params);

                params = mCamera.getParameters();

                mFrameWidth = params.getPreviewSize().width;
                mFrameHeight = params.getPreviewSize().height;

                int size = mFrameWidth * mFrameHeight;
                size = size
                        * ImageFormat
                                .getBitsPerPixel(params.getPreviewFormat()) / 8;
                mBuffer = new byte[size];

                mFrame = new Mat(mFrameHeight + (mFrameHeight / 2),
                        mFrameWidth, CvType.CV_8UC1);
                mCameraFrame = new CameraAccessFrame(mFrame, mFrameWidth,
                        mFrameHeight);

                SurfaceTexture texture = new SurfaceTexture(0);

                try {
                    mCamera.setPreviewTexture(texture);
                    mCamera.addCallbackBuffer(mBuffer);
                    mCamera.setPreviewCallbackWithBuffer(this);
                    mCamera.startPreview();

                    Log.d(TAG, "Camera preview started");
                } catch (Exception e) {
                    Log.d(TAG,
                            "Error starting camera preview: " + e.getMessage());
                }

                for (CameraFrameCallback callback : mCallbacks)
                    callback.onCameraInitialized(mFrameWidth, mFrameHeight);
            }
        }
    }

    private void releaseCamera() {
        synchronized (this) {
            if (mCamera != null) {
                mCamera.stopPreview();
                mCamera.setPreviewCallback(null);

                mCamera.release();

                Log.d(TAG, "Preview stopped and camera released");
            }
            mCamera = null;

            if (mFrame != null) {
                mFrame.release();
            }

            if (mCameraFrame != null) {
                mCameraFrame.release();
            }

            for (CameraFrameCallback callback : mCallbacks)
                callback.onCameraReleased();
        }
    }

    private class CameraAccessFrame implements CameraFrame {
        private Mat mYuvFrameData;
        private Mat mRgba;
        private int mWidth;
        private int mHeight;
        private Bitmap mCachedBitmap;
        private boolean mRgbaConverted;
        private boolean mBitmapConverted;

        @Override
        public Mat gray() {
            return mYuvFrameData.submat(0, mHeight, 0, mWidth);
        }

        @Override
        public Mat rgba() {
            if (!mRgbaConverted) {
                Imgproc.cvtColor(mYuvFrameData, mRgba,
                        Imgproc.COLOR_YUV2BGR_NV12, 4);
                mRgbaConverted = true;
            }
            return mRgba;
        }

        @Override
        public Bitmap toBitmap() {
            if (mBitmapConverted)
                return mCachedBitmap;

            Mat rgba = this.rgba();
            Utils.matToBitmap(rgba, mCachedBitmap);
            mBitmapConverted = true;
            return mCachedBitmap;
        }

        public CameraAccessFrame(Mat Yuv420sp, int width, int height) {
            super();
            mWidth = width;
            mHeight = height;
            mYuvFrameData = Yuv420sp;
            mRgba = new Mat();

            this.mCachedBitmap = Bitmap.createBitmap(width, height,
                    Bitmap.Config.ARGB_8888);
        }

        public void release() {
            mRgba.release();
            mCachedBitmap.recycle();
        }

        public void invalidate() {
            mRgbaConverted = false;
            mBitmapConverted = false;
        }
    };

    public interface CameraFrameCallback {
        void onCameraInitialized(int frameWidth, int frameHeight);

        void onFrameReceived(CameraFrame frame);

        void onCameraReleased();
    }

    @Override
    public void onManagerConnected(int status) {
        mOpenCVloaded = true;

        if (mCallbacks.size() > 0)
            connectCamera();
    }

    @Override
    public void onPackageInstall(int operation,
            InstallCallbackInterface callback) {
    }

    private class PreviewSizeComparer implements Comparator<Camera.Size> {
        @Override
        public int compare(Size arg0, Size arg1) {
            if (arg0 != null && arg1 == null)
                return -1;
            if (arg0 == null && arg1 != null)
                return 1;

            if (arg0.width < arg1.width)
                return -1;
            else if (arg0.width > arg1.width)
                return 1;
            else
                return 0;
        }

    }
}

public class CameraCanvasView extends SurfaceView implements CameraFrameCallback, SurfaceHolder.Callback {

    Context context;
    CameraAccess mCamera;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Rect mBackgroundSrc = new Rect();

    public CameraCanvasView(Context context) {
        super(context);

        this.context = context;

        SurfaceHolder sh = this.getHolder();
        sh.addCallback(this);

        setFocusable(true);

        this.mCamera = CameraAccess.getInstance(context,
                CameraInfo.CAMERA_FACING_BACK);
    }

    @Override
    public void onCameraInitialized(int frameWidth, int frameHeight) {
    }

    @Override
    public void onFrameReceived(CameraFrame frame) {
        this.setBackgroundImage(frame.toBitmap());
    }

    @Override
    public void onCameraReleased() {

        setBackgroundImage(null);
    }

    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        this.setWillNotDraw(false);
        this.mCamera.addCallback(this);
    }

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

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        this.mCamera.removeCallback(this);
    }

    public void setBackgroundImage(Bitmap image) {
        this.mBackground = image;

        if (image != null)
            this.mBackgroundSrc.set(0, 0, image.getWidth(), image.getHeight());
        else
            this.mBackgroundSrc.setEmpty();

        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas) {
        canvas.drawColor(Color.BLACK);

        if (mBackground != null && !mBackground.isRecycled())
            canvas.drawBitmap(mBackground, mBackgroundSrc, boundingBox, paint);
    }
}
  • 0
    Привет @Matthias Я занимаюсь разработкой приложения CustomCaemra. У меня была та же проблема. Я также хочу предварительный просмотр нескольких камер в одном действии [Как в GridView]. Пожалуйста, помогите мне Как я могу добиться этого ... я уже задавал вопрос, но никто не дал мне ответа .... Вот моя ссылка на вопрос. ...... stackoverflow.com/questions/41392791/…
  • 0
    я прогуглил это и попробовал больше учебника, библиотек [grafica too]. но я не достиг этого вида Preview [Multiple Cmaera Preview]. я обнаружил, что проблема в том же, что и моя проблема, в Stackoverflow.Но так же, как и на мой вопрос, никто не дал ответа. Пожалуйста, посмотрите эту ссылку, я хочу этот тип просмотра .... i.stack.imgur.com/WYHuj.jpg
Показать ещё 7 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню