Android视频处理 - 如何将ImageReader曲面连接到预览?

时间:2017-09-22 10:43:47

标签: android android-camera2 textureview

我使用Android的Camera2 API,想在相机预览帧上执行一些图像处理,然后在预览中显示更改(TextureView)。

从常见的camera2video示例开始,我在openCamera()中设置了ImageReader。

    mImageReader = ImageReader.newInstance(mVideoSize.getWidth(),
    mVideoSize.getHeight(), ImageFormat.YUV_420_888, mMaxBufferedImages);
    mImageReader.setOnImageAvailableListener(mImageAvailable, mBackgroundHandler);

在我的startPreview()中,我已将Surfaces设置为从CaptureRequest接收帧。

    SurfaceTexture texture = mTextureView.getSurfaceTexture();
    assert texture != null;
    texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
    mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

    List<Surface> surfaces = new ArrayList<>();

    // Here is where we connect the mPreviewSurface to the mTextureView.
    mPreviewSurface = new Surface(texture);
    surfaces.add(mPreviewSurface);
    mPreviewBuilder.addTarget(mPreviewSurface);

    // Connect our Image Reader to the Camera to get the preview frames.
    Surface readerSurface = mImageReader.getSurface();
    surfaces.add(readerSurface);
    mPreviewBuilder.addTarget(readerSurface);

然后我将修改OnImageAvailableListener()回调中的图像数据。

ImageReader.OnImageAvailableListener mImageAvailable = new ImageReader.OnImageAvailableListener() {
    @Override
    public void onImageAvailable(ImageReader reader) {
        try {
            Image image = reader.acquireLatestImage();
            if (image == null)
                return;

            final Image.Plane[] planes = image.getPlanes();

            // Do something to the pixels.
            // Black out part of the image.
            ByteBuffer y_data_buffer = planes[0].getBuffer();
            byte[] y_data = new byte[y_data_buffer.remaining()];
            y_data_buffer.get(y_data);
            byte y_value;
            for (int row = 0; row < image.getHeight() / 2; row++) {
                for (int col = 0; col < image.getWidth() / 2; col++) {
                    y_value = y_data[row * image.getWidth() + col];
                    y_value = 0;
                    y_data[row * image.getWidth() + col] = y_value;
                }
            }

            image.close();
        } catch (IllegalStateException e) {
            Log.d(TAG, "mImageAvailable() Too many images acquired");
        }
    }
};

据我了解,我现在将图像发送到2个Surface实例,一个用于mTextureView,另一个用于我的ImageReader。

如何让我的mTextureView使用与ImageReader相同的Surface,或者我应该直接从mTextureView的Surface操作图像数据?

由于

1 个答案:

答案 0 :(得分:1)

如果您只想显示修改后的输出,那么我不确定为什么要配置两个输出(TextureView和ImageReader)。

一般来说,如果你想要像

这样的东西
camera -> in-app edits -> display

您有多种选择,具体取决于您想要的编辑类型,以及编码的简易性,性能等之间的各种权衡。

最有效的选项之一是将您的编辑作为OpenGL着色器。 在这种情况下,GLSurfaceView可能是最简单的选择。 创建一个SurfaceTexture对象,其纹理ID在GLSurfaceView的EGL上下文中未使用,并将SurfaceTexture创建的Surface传递给相机会话和请求。 然后在SurfaceView绘图方法中,调用SurfaceTexture的updateTexImage()方法,然后使用纹理ID根据需要渲染输出。

这确实需要很多OpenGL代码,所以如果你不熟悉它,那就很有挑战性。

您也可以使用RenderScript获得类似的效果;在那里你将有一个输出SurfaceView或TextureView,然后是一个RenderScript脚本,它从Camera的输入Allocation中读取并写入到View的输出Allocation;您可以从Surface创建此类分配。 Google HdrViewfinderDemo camera2示例应用使用此方法。它的样板很少。

第三,您可以像现在一样使用ImageReader,但是您必须自己进行大量转换才能将其写入屏幕。最简单(但最慢)的选项是从SurfaceView或ImageView获取Canvas,然后逐个写入像素。或者你可以通过ANativeWindow NDK来做到这一点,这更快但是需要编写JNI代码并且仍然需要你自己进行YUV-> RGB转换(或者使用未记录的API将YUV推入ANativeWindow并希望它能够工作)。