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

标签 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 操作图像数据?

谢谢

最佳答案

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

一般来说,如果你想要类似的东西

camera -> in-app edits -> display

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

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

这确实需要大量 OpenGL 代码,所以如果您不熟悉它,那可能会很有挑战性。

您也可以使用 RenderScript 获得类似的效果;在那里您将有一个输出 SurfaceView 或 TextureView,然后是一个 RenderScript 脚本,该脚本从相机的输入分配中读取并将输出分配写入 View ;您可以从表面创建此类分配。 谷歌HdrViewfinderDemo camera2 示例应用程序使用这种方法。样板文件要少得多。

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

关于Android 视频处理 - 如何将 ImageReader Surface 连接到预览?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46362835/

相关文章:

android - 将视频从 Android 应用程序导出到 SD 卡

android - Android 中的自定义字体 - 简单而便宜的方法

android - 如何解决java.util.zip.ZipException duplicate entry : com/google/gson/annotations/Expose.类?

android - 使用 CameraX 或 Camera2 获取相机 ISO/快门速度

java - Android Camera2 - 切换闪光模式

android - 如何在android.view.TextureView中绘制opengl Cube

java - 在前置摄像头拍摄的 TextureView 上播放视频

android - 如何在也有 ApplicationContext 对象的简单 Java 类中访问 Android 自定义应用程序对象?

java - 从 Firebase 设置 AutoCompleteTextView Material 下拉列表的选定值

android - 三星 Galaxy S7 (Camera2) 上的 YUV_420_888 解读