SurfaceTexture与camera preview绑定

1. 核心概念

SurfaceTexture 是 Android 中的一个类,它允许你从某些图像源(例如相机预览)捕获帧,并将这些帧作为纹理(Texture)渲染到 OpenGL ES 环境中。它通常用于需要在自定义渲染管道中显示摄像头预览的场景。

  • 纹理(Texture)SurfaceTexture 将图像帧作为 OpenGL ES 纹理,方便与 OpenGL 进行高效的图像处理。
  • 帧缓冲区(Frame Buffer)SurfaceTexture 内部维护着一个帧缓冲区,从源(如相机)接收帧。
  • OpenGL 与 SurfaceTexture 结合:通常,SurfaceTexture 会与 OpenGL 一起使用,以便更高效地渲染和处理图像帧。

2. 主要功能

  • 用于相机预览:常用于相机 API 中的自定义预览(比如将相机帧直接传输到 OpenGL 纹理中)。
  • 低延迟处理:与 OpenGL 结合时,它能够实现低延迟的图像流处理。
  • 同步处理:提供了对帧同步的支持,通过 updateTexImage() 同步纹理。

3. 关键方法

1. 构造方法
  • SurfaceTexture(int texName):创建一个 SurfaceTexture,并将其与指定的 OpenGL ES 纹理 ID 绑定(texName 是纹理的句柄)。
2. 绑定 SurfaceTexture 与 Surface
  • Surface getSurface():获取与此 SurfaceTexture 关联的 Surface 对象,可以将其传递给相机或视频解码器等,用于图像捕获。
  • setDefaultBufferSize(int width, int height):设置 SurfaceTexture 的默认缓冲区大小,这通常是预览帧的大小。
3. 处理图像帧
  • updateTexImage():从缓冲区中更新当前的纹理图像。这个方法通常在每一帧图像到来时调用,确保纹理内容被更新。
  • getTimestamp():获取当前纹理图像的时间戳,通常用于同步图像帧和其他信号。
  • release():释放 SurfaceTexture 占用的资源,通常在 SurfaceTexture 不再需要时调用。
4. 监听帧更新
  • setOnFrameAvailableListener(SurfaceTexture.OnFrameAvailableListener listener):设置帧可用时的监听器。当新的图像帧可用时,系统会调用此监听器。
surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        // 在新的图像帧到达时回调
    }
});

4. 使用流程

1. 创建 SurfaceTexture 对象

首先,通过创建 SurfaceTexture 并传递一个 OpenGL 纹理 ID 来初始化它。

int[] texture = new int[1];
GLES20.glGenTextures(1, texture, 0); // 创建一个 OpenGL 纹理
SurfaceTexture surfaceTexture = new SurfaceTexture(texture[0]); // 绑定这个纹理
2. 设置默认缓冲区大小

接下来,通常会设置 SurfaceTexture 的默认缓冲区大小,通常是相机预览的分辨率大小。

surfaceTexture.setDefaultBufferSize(1920, 1080); // 设置为 1920x1080 的分辨率
3. 获取 Surface 对象

你可以通过 SurfaceTexture.getSurface() 获取 Surface 对象,并将其传递给相机进行图像流的捕获。

Surface surface = new Surface(surfaceTexture);
camera.setPreviewSurface(surface); // 将 Surface 传递给相机
4. 同步纹理图像

在每一帧到达时,你需要调用 updateTexImage() 来同步纹理,并将其更新到 OpenGL 的纹理缓冲区中。

surfaceTexture.updateTexImage(); // 更新纹理图像
5. 释放资源

在不需要 SurfaceTexture 时,调用 release() 方法来释放它占用的资源。

surfaceTexture.release(); // 释放资源

5. 完整的示例代码

下面是一个完整的示例,展示如何使用 SurfaceTexture 来处理相机预览,并将其作为 OpenGL 纹理渲染。

public class MyGLRenderer implements GLSurfaceView.Renderer {

    private SurfaceTexture surfaceTexture;
    private int[] textures;

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        // 创建 OpenGL 纹理
        textures = new int[1];
        GLES20.glGenTextures(1, textures, 0);
        
        // 创建 SurfaceTexture 并绑定到 OpenGL 纹理
        surfaceTexture = new SurfaceTexture(textures[0]);
        
        // 设置帧可用监听器
        surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
            @Override
            public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                // 当新的帧可用时处理
            }
        });

        // 设置默认缓冲区大小
        surfaceTexture.setDefaultBufferSize(1920, 1080);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        // 更新纹理内容
        surfaceTexture.updateTexImage();
        
        // 继续绘制
        // GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
        // 渲染代码
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
    }
}

6. 典型应用场景

  • 相机预览:可以将相机预览直接显示到 GLSurfaceView 上。
  • 视频解码:可以用 SurfaceTexture 来渲染解码后的视频帧到自定义 OpenGL 表面。
  • 图像处理:通过将图像帧渲染到 OpenGL 中,进行高效的图像处理和特效渲染。

总结

SurfaceTexture 提供了从视频源(如相机)到 OpenGL 纹理的高效转换,使得图像流可以被更灵活地处理和渲染。在使用 OpenGL 进行渲染时,它是处理视频流的核心工具之一。

TexturePreview与预览绑定:

    @NonNull
    private OutputConfiguration getOutputConfiguration(SurfaceTexture surfaceTexture, Size previewSize) {
        OutputConfiguration texPreviewReader = null;
        if (surfaceTexture == null) {
            texPreviewReader = new OutputConfiguration(previewSize, SurfaceTexture.class);
        } else {
            final Surface surface = new Surface(surfaceTexture);
            texPreviewReader = new OutputConfiguration(surface);
            if (mSurface != null) {
                mSurface.release();
            }
            mSurface = surface;
        }
        return texPreviewReader;
    }



public Surface configPreview(CameraCaptureSession captureSession,
                                 CaptureRequest.Builder previewRequestBuilder,
                                 List<OutputConfiguration> outputConfigs,
                                 SurfaceTexture surfaceTexture) {
        LogUtil.v(TAG, "configPreview: ");
        Surface texturePreviewSurface = null;

        try {
            if (captureSession ==null || outputConfigs == null || surfaceTexture == null) {
                return null;
            }

            boolean needFinalizeOutputConfig = false;
            for (OutputConfiguration outputConfig : outputConfigs) {
                Surface surface = outputConfig.getSurface();
                if (surface == null) {
                    texturePreviewSurface = new Surface(surfaceTexture);
                    outputConfig.addSurface(texturePreviewSurface);
                    needFinalizeOutputConfig = true;
                    break;
                }
            }

            if (needFinalizeOutputConfig) {
                captureSession.finalizeOutputConfigurations(outputConfigs);
            }
        } catch (CameraAccessException | IllegalStateException e) {
            LogUtil.e(TAG, "finalizePreviewOutputConfigs", e);
        }

        if (texturePreviewSurface != null) {
            if (mSurface != null) {
                mSurface.release();
            }
            mSurface = texturePreviewSurface;

        }
        previewRequestBuilder.addTarget(mSurface);
        return mSurface;
    }
texturePreviewSurface = new Surface(surfaceTexture);
outputConfig.addSurface(texturePreviewSurface);
mSurface = texturePreviewSurface;
previewRequestBuilder.addTarget(mSurface);

两者用通过surfaceTexture 进行绑定,相机获取到的图像直接传送到SurfaceTexture 内部维护着一个帧缓冲区。

然后在每一帧到达后需要用updateTexImage() 来同步纹理,并将其更新到 OpenGL 的纹理缓冲区中。

protected void updateSurfaceTextureImage() {
    try {
        mSurfaceTexture.updateTexImage();
    } catch (RuntimeException e) {
        LogUtil.e(TAG, "updateTexImage failed, e = ", e);
    }
}
进阶:预览图像算法处理后在显示

要是我们需要将图片处理一遍后在显示。那么就不能直接将surfaceTexture获得的surface与相机输出流进行绑定了。
不然我们就无法拿到image 来操作image,只能在这里处理纹理,不是我们想要的。(简单的滤镜可以在这里渲染)

    @Override
    public void onDrawFrame(GL10 gl) {
        // 更新纹理内容
        surfaceTexture.updateTexImage();
        
        // 继续绘制
        // GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
        // 渲染代码
    }

所以我们应该需要怎么操作,此时有个关键的类ImageWriter(之前的文章有过介绍,这里就不过多的解释了)中的queueInputImage方法:

public void queueInputImage(Image image) {}

mImageWriter.queueInputImage(image);

queueInputImage(image) 的作用是将一个 Image 对象传递给 ImageWriter,并将其插入到 ImageWriter 的缓冲队列中。这个方法会将 Image 添加到队列里,等待将图像写入到它关联的 Surface 上。

上面方法可以将处理后的image 添加到suerface中的帧缓存区中,那么我们在使用ImageWriter.newInstance 使之与surfaceTexture进行绑定

public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
            @IntRange(from = 1) int maxImages, @Format int format);
    
mImageWriter = ImageWriter.newInstance(surfaceTexture, mYuvPreview.getMaxImage(), ImageFormat.YUV_420_888);
示例:(假设已经有了yuvpreview)
        final Surface surface = new Surface(surfaceTexture);
mImageWriter = ImageWriter.newInstance(surface, mYuvPreview.getMaxImage(), ImageFormat.YUV_420_888);

mYuvPreview.configPreview(previewRequestBuilder, new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader reader) {
                final Image image = reader.acquireLatestImage();
                if (image != null) {
                    if (mImageWriter != null) {
                        int orientation = mModuleReference.getSettingProxy().getDisplayOrientation();
                        if (isFrontCam()) {
                            switch (orientation % 360) {
                                case 0: orientation = 270; break;
                                case 90: orientation = 180; break;
                                case 180: orientation = 90; break;
                                case 270: orientation = 0; break;
                            }
                        } else {
                            orientation += 90;
                        }

                        int imageSize = image.getPlanes()[0].getRowStride() * image.getHeight();
                        int yuvSize = imageSize * 3 / 2;
                        BeautyShotUtil.ProcessPreview(image.getPlanes()[0].getBuffer(),
                                image.getPlanes()[1].getBuffer(), image.getPlanes()[2].getBuffer(),
                                null, false, image.getWidth(), image.getHeight(),
                                image.getPlanes()[0].getRowStride(), image.getPlanes()[1].getRowStride(),
                                image.getPlanes()[1].getPixelStride(), orientation);
                        mImageWriter.queueInputImage(image);
                    } else {
                        image.close();
                    }
                }
            }
        }, mPreviewHandler);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值