Android OpenGL開発-詳細なカメラプレビュー(デモ付き)

以前はカメラをカスタマイズする必要がありましたが、以前はサードパーティを使用していましたが、その後需要が変化しました。サードパーティはダイナミックステッカーの追加をサポートしていないため、単独で拡張することしかできません。もちろんインターネット上にはたくさんの例がありますが、ダイナミックステッカーを貼る例はほとんどなく、とにかく見つかりませんでした(涙を流さずに泣きたい)。もちろん、すべての機能が実装されました。openGlは非常に興味深いと思うので、最初からもう一度整理して、OpenGL学習シリーズを編成する予定です。参考のために。OpenGlについて何も知らない場合は、 #Android OpenGL開発を読むことをお勧めします-画像描画の詳細な説明完全なプロジェクトリンク:AndroidCamera

さて、これ以上面倒なことはせずに、テキストを入力しましょう:

この記事から次の知識を学ぶことができます。

  1. SurfaceView、GlSurfaceView、SurfaceTexture、TextureViewの長所、短所、および相違点。
  2. SurfaceViewを介してカメラプレビューを表示する方法。
  3. TextureViewを介してカメラプレビューを表示する方法。
  4. GlSurfaceViewを介してカメラプレビューを処理する方法。
  5. 記事の概要
  6. ソースリンク

1. SurfaceView、GlSurfaceView、SurfaceTexture、TextureViewの長所、短所、および相違点

SurfaceView

ビューから継承され、ビューのほとんどのプロパティがありますが、ホルダーが存在するため、透明度を設定できません。利点:ダブルバッファリングメカニズムを使用して、メインスレッドに影響を与えることなく、別のスレッドで描画できるため、ビデオの再生時に画面がスムーズになります。ネストできません。

GlSurfaceView

GlSurfaceViewは、OpenGLレンダリングを表示するために特別に使用されるSurfaceViewクラスを継承します。これは、ビデオ、画像、および3Dシーンを表示できることを理解するのは簡単です。

表面のテクスチャ

和SurfaceView功能类似,区别是,SurfaceTexure可以不显示在界面中。使用OpenGl对图片流进行美化,添加水印,滤镜这些操作的时候我们都是通过SurfaceTexre去处理,处理完之后再通过GlSurfaceView显示。缺点,可能会导致个别帧的延迟。本身管理着BufferQueue,所以内存消耗会多一点。

TextureView

同样继承自View,必须在开启硬件加速的设备中使用(保守估计目前百分之九十的Android设备都开启了),TextureView通过setSurfaceTextureListener的回调在子线程中进行更新UI. 优点:支持动画效果。 缺点:在5.0之前在主线程渲染,在5.0之后在单独线程渲染。

TextureView SurfaceView
绘制 稍微延时 及时
内存
动画 支持 不支持
耗电
适用场景(推荐) 视频播放,相机应用 大量画布更新(游戏绘制)

2. 如何通过SurfaceView显示Camera预览。

基本步骤

  1. 在xml文件中设置SurfaceView 。
  2. 实现SurfaceHolder.Callback的回调。
  3. 打开摄像头Camera.open(0);
  4. 设置摄像头相关参数;
  5. 将摄像头数据设置到SurfaceView中,并开启预览。

代码部分

<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"  tools:context="com.aserbao.aserbaosandroid.opengl.openGlCamera.simpleCameraOpengl.simpleCamera.CameraSurfaceViewShowActivity">
    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <SurfaceView
                    android:layout_width="match_parent"
                     android:layout_height="match_parent"
                     android:id="@+id/mSurface" />
        <Button
            android:id="@+id/btn_change"
            android:text="给动画,SurfaceView不支持设置透明度"
            android:textAllCaps="false"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </FrameLayout>
</android.support.constraint.ConstraintLayout>
/**
 * 使用SurfaceView预览Camera数据
 */
public class CameraSurfaceViewShowActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    @BindView(R.id.mSurface)
    SurfaceView mSurfaceView;

    public SurfaceHolder mHolder;
    private Camera mCamera;
    private Camera.Parameters mParameters;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base_camera);
        ButterKnife.bind(this);
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // Open the Camera in preview mode
            mCamera = Camera.open(0);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if (success) {
                    mParameters = mCamera.getParameters();
                    mParameters.setPictureFormat(PixelFormat.JPEG); //图片输出格式
//                    mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//预览持续发光
                    mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//持续对焦模式
                    mCamera.setParameters(mParameters);
                    mCamera.startPreview();
                    mCamera.cancelAutoFocus();
                }
            }
        });
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    @OnClick(R.id.btn_change)
    public void onViewClicked() {
//        PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("rotationY", 0.0f, 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.5f,1.0f);
        PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.5f,1.0f);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mSurfaceView,  valuesHolder,valuesHolder1,valuesHolder3);
        objectAnimator.setDuration(5000).start();
    }
}

效果展示

特别提醒

SurfaceView预览相机视图不支持透明度,可以设置缩放旋转属性。如果需要做动画特效的话不推荐使用SurfaceView显示视图。可以使用下面的TextureView或者GlSurfaceView来显示。

3. 如何通过TextureView显示Camera预览

基本步骤

TextureView和SurfaceView显示Camera数据其实差不多,差别就两点:

  1. SurfaceView显示需要实现SurfaceHolder.Callback的回调而TextureView通过实现 TextureView.SurfaceTextureListener接口。
  2. 当Camera使用SurfaceView预览时通过setPreviewDisplay(holder)方法来设置预览视图,而使用TextureView预览时使用setPreviewTexture(mCameraTextureView.getSurfaceTexture())方法来设置。 其他步骤同上。

代码部分

public class CameraTextureViewShowActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {

    @BindView(R.id.camera_texture_view)
    TextureView mCameraTextureView;
    public Camera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_surface_texture);
        ButterKnife.bind(this);
        mCameraTextureView.setSurfaceTextureListener(this);
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        try {
            mCamera = Camera.open(0);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewTexture(mCameraTextureView.getSurfaceTexture());
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    @OnClick(R.id.btn_texture_anim)
    public void onViewClicked() {
        PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("translationX", 0.0f, 0.0f);
        PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.3f,1.0f);
        PropertyValuesHolder valuesHolder4 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.3f,1.0f);
        PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 2 * 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder5 = PropertyValuesHolder.ofFloat("rotationY", 0.0f, 2 * 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.7f, 1.0F);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mCameraTextureView, valuesHolder, valuesHolder1, valuesHolder2, valuesHolder3,valuesHolder4,valuesHolder5);
        objectAnimator.setDuration(5000).start();
    }
}

效果展示

4. 如何通过GlSurfaceView处理Camera预览。

如果你在学习自定义相机,而且你的相机想要实现美颜,滤镜,人脸识别AR场景 and so on。这时候你就必须要学习如何使用GlsurfaView罗。如果你没有openGl的基本配置的知识或者你之前完全没有学习过openGl的开发,再次强烈建议你看一下这篇文章# Android OpenGL开发——图像绘制详解,否则,下面内容可能会引起你的严重不适。当然,如果你还是觉得哪部分写得不好或者有疑问的话,欢迎给我留言,或者关注公众号aserbao私聊我。

基本步骤

  1. 在xml中添加GlSurfaceView
  2. 创建渲染器类实现GlSurfaceView.Renderer
  3. 清除画布,并创建一个纹理并绑定到。
  4. 创建一个用来最后显示的SurfaceTexture来显示处理后的数据。
  5. 创建Opengl ES程序并添加着色器到该程序中,创建openGl程序的可执行文件,并释放shader资源。
  6. 打开摄像头,并配置相关属性。设置预览视图,并开启预览。
  7. 添加程序到ES环境中,并设置及启用各类句柄。
  8. 在onDrawFrame中进行画布的清理及绘制最新的数据到纹理图形中。
  9. 设置一个SurfaceTexture.OnFrameAvailableListener的回调来通知GlSurfaceview渲染新的帧数据。

建议: GlSurfaceView作用简单的理解OpenGl对相机数据进行处理完之后的显示。我们需要明白的是渲染器的渲染周期及渲染方法的调用时机。

  1. onSurfaceCreated()当surface创建(第一次进入当前页面)或者重新创建(切换后台再进入)的时候调用。
  2. onSurfaceChanged()当surface大小发生改变的时候会被调用。
  3. onDrawFrame()绘制当前帧数据的时候被调用。

代码部分

public class CameraGlSurfaceShowActivity extends AppCompatActivity implements SurfaceTexture.OnFrameAvailableListener {
    public SurfaceTexture mSurfaceTexture;

    public static Camera camera;
    private int camera_status = 1;
    @BindView(R.id.camera_glsurface_view)
    GLSurfaceView mCameraGlsurfaceView;
    public MyRender mRenderer;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_gl_surface_show);
        ButterKnife.bind(this);
        mCameraGlsurfaceView.setEGLContextClientVersion(2);//在setRenderer()方法前调用此方法
        mRenderer = new MyRender();
        mCameraGlsurfaceView.setRenderer(mRenderer);
        mCameraGlsurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
    }


    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        mCameraGlsurfaceView.requestRender();
    }

    @OnClick({R.id.btn_gl_surface_view_animator, R.id.btn_gl_surface_view_switch})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_gl_surface_view_animator:
                PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.5f,1.0f);
                PropertyValuesHolder valuesHolder4 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.5f,1.0f);
                PropertyValuesHolder valuesHolder5 = PropertyValuesHolder.ofFloat("rotationY", 0.0f, 360.0f, 0.0F);
                ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mCameraGlsurfaceView, valuesHolder1, valuesHolder4,valuesHolder5);
                objectAnimator.setDuration(3000).start();
                break;
            case R.id.btn_gl_surface_view_switch:
                camera_status ^= 1;
                if (camera != null) {
                    camera.stopPreview();
                    camera.release();
                }
                mRenderer.mBoolean = true;
                camera = Camera.open(camera_status);
                try {
                    camera.setPreviewTexture(mSurfaceTexture);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                camera.startPreview();
                break;
        }
    }

    public class MyRender implements GLSurfaceView.Renderer {
        private final String vertexShaderCode = "uniform mat4 textureTransform;\n" +
                "attribute vec2 inputTextureCoordinate;\n" +
                "attribute vec4 position;            \n" +//NDK坐标点
                "varying   vec2 textureCoordinate; \n" +//纹理坐标点变换后输出
                "\n" +
                " void main() {\n" +
                "     gl_Position = position;\n" +
                "     textureCoordinate = inputTextureCoordinate;\n" +
                " }";

        private final String fragmentShaderCode = "#extension GL_OES_EGL_image_external : require\n" +
                "precision mediump float;\n" +
                "uniform samplerExternalOES videoTex;\n" +
                "varying vec2 textureCoordinate;\n" +
                "\n" +
                "void main() {\n" +
                 "    vec4 tc = texture2D(videoTex, textureCoordinate);\n" +
                "    float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +  //所有视图修改成黑白
                "    gl_FragColor = vec4(color,color,color,1.0);\n" +
//                "    gl_FragColor = vec4(tc.r,tc.g,tc.b,1.0);\n" +
                "}\n";
        private FloatBuffer mPosBuffer;
        private FloatBuffer mTexBuffer;
        private float[] mPosCoordinate = {-1, -1, -1, 1, 1, -1, 1, 1};
        private float[] mTexCoordinateBackRight = {1, 1, 0, 1, 1, 0, 0, 0};//顺时针转90并沿Y轴翻转  后摄像头正确,前摄像头上下颠倒
        private float[] mTexCoordinateForntRight = {0, 1, 1, 1, 0, 0, 1, 0};//顺时针旋转90  后摄像头上下颠倒了,前摄像头正确

        public int mProgram;
        public boolean mBoolean = false;

        public MyRender() {
            Matrix.setIdentityM(mProjectMatrix, 0);
            Matrix.setIdentityM(mCameraMatrix, 0);
            Matrix.setIdentityM(mMVPMatrix, 0);
            Matrix.setIdentityM(mTempMatrix, 0);
        }

        private int loadShader(int type, String shaderCode) {
            int shader = GLES20.glCreateShader(type);
            // 添加上面编写的着色器代码并编译它
            GLES20.glShaderSource(shader, shaderCode);
            GLES20.glCompileShader(shader);
            return shader;
        }

        private void creatProgram() {
            //通常做法
//            String vertexSource = AssetsUtils.read(CameraGlSurfaceShowActivity.this, "vertex_texture.glsl");
//            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
//            String fragmentSource = AssetsUtils.read(CameraGlSurfaceShowActivity.this, "fragment_texture.glsl");
//            int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
            int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
            // 创建空的OpenGL ES程序
            mProgram = GLES20.glCreateProgram();

            // 添加顶点着色器到程序中
            GLES20.glAttachShader(mProgram, vertexShader);

            // 添加片段着色器到程序中
            GLES20.glAttachShader(mProgram, fragmentShader);

            // 创建OpenGL ES程序可执行文件
            GLES20.glLinkProgram(mProgram);

            // 释放shader资源
            GLES20.glDeleteShader(vertexShader);
            GLES20.glDeleteShader(fragmentShader);
        }

        private FloatBuffer convertToFloatBuffer(float[] buffer) {
            FloatBuffer fb = ByteBuffer.allocateDirect(buffer.length * 4)
                    .order(ByteOrder.nativeOrder())
                    .asFloatBuffer();
            fb.put(buffer);
            fb.position(0);
            return fb;
        }

        private int uPosHandle;
        private int aTexHandle;
        private int mMVPMatrixHandle;
        private float[] mProjectMatrix = new float[16];
        private float[] mCameraMatrix = new float[16];
        private float[] mMVPMatrix = new float[16];
        private float[] mTempMatrix = new float[16];

        //添加程序到ES环境中
        private void activeProgram() {
            // 将程序添加到OpenGL ES环境
            GLES20.glUseProgram(mProgram);

            mSurfaceTexture.setOnFrameAvailableListener(CameraGlSurfaceShowActivity.this);
            // 获取顶点着色器的位置的句柄
            uPosHandle = GLES20.glGetAttribLocation(mProgram, "position");
            aTexHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "textureTransform");

            mPosBuffer = convertToFloatBuffer(mPosCoordinate);
            if(camera_status == 0){
                mTexBuffer = convertToFloatBuffer(mTexCoordinateBackRight);
            }else{
                mTexBuffer = convertToFloatBuffer(mTexCoordinateForntRight);
            }

            GLES20.glVertexAttribPointer(uPosHandle, 2, GLES20.GL_FLOAT, false, 0, mPosBuffer);
            GLES20.glVertexAttribPointer(aTexHandle, 2, GLES20.GL_FLOAT, false, 0, mTexBuffer);

            // 启用顶点位置的句柄
            GLES20.glEnableVertexAttribArray(uPosHandle);
            GLES20.glEnableVertexAttribArray(aTexHandle);
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            mSurfaceTexture = new SurfaceTexture(createOESTextureObject());
            creatProgram();
//            mProgram = ShaderUtils.createProgram(CameraGlSurfaceShowActivity.this, "vertex_texture.glsl", "fragment_texture.glsl");
            camera = Camera.open(camera_status);
            try {
                camera.setPreviewTexture(mSurfaceTexture);
                camera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }
            activeProgram();

        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
            Matrix.scaleM(mMVPMatrix,0,1,-1,1);
            float ratio = (float) width / height;
            Matrix.orthoM(mProjectMatrix, 0, -1, 1, -ratio, ratio, 1, 7);// 3和7代表远近视点与眼睛的距离,非坐标点
            Matrix.setLookAtM(mCameraMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);// 3代表眼睛的坐标点
            Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mCameraMatrix, 0);
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            if(mBoolean){
                activeProgram();
                mBoolean = false;
            }
            if (mSurfaceTexture != null) {
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
                mSurfaceTexture.updateTexImage();
                GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
                GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mPosCoordinate.length / 2);
            }
        }
    }

    public static int createOESTextureObject() {
        int[] tex = new int[1];
        //生成一个纹理
        GLES20.glGenTextures(1, tex, 0);
        //将此纹理绑定到外部纹理上
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]);
        //设置纹理过滤参数
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        return tex[0];
    }
}

当然,大多数情况下渲染顶点着色器及片段着色器的代码会编写一个glsl的文件放到assets目录下进行访问。 下面是另外一个操作方式:

vertex_texture.glsl文件

uniform mat4 textureTransform;
attribute vec2 inputTextureCoordinate;
attribute vec4 position;            //NDK坐标点
varying   vec2 textureCoordinate; //纹理坐标点变换后输出

 void main() {
     gl_Position = position;
     textureCoordinate = inputTextureCoordinate;
 }

fragment_texture.glsl文件:

#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES videoTex;
varying vec2 textureCoordinate;

void main() {
    vec4 tc = texture2D(videoTex, textureCoordinate);
    float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;//这里进行的颜色变换处理,传说中的黑白滤镜。
    gl_FragColor = vec4(color,color,color,1.0);
}

读取文件内容方式:

public static String read(Context context, String fileName) {
        String result = null;
        try {
            InputStream is = context.getResources().getAssets().open("Shader/" + fileName);
            int length = is.available();
            byte[] buffer = new byte[length];
            is.read(buffer);
            result = new String(buffer, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

具体实现在上面代码creatProgram()下注释掉通常做法的那部分。

实现效果

遇到的问题

  1. 使用OpenGl处理数据之后再GlsurfaceView上图片镜像了,咋办?这种问题要么出在绘制顶点坐标,要么出在源数据上,但是通过其他的比如SurfaceView直接预览数据没问题,那么只能是第一种可能了。仔细回看下代码,果然修改渲染颜色矩阵显示出来的数据方向都是不同的。

下面是我改动矩阵产生的结果:

问题根本找到了,如何解决呢?起始点就四个点,一个点就最多三个连接点,而且起始点不能和不相邻的点进行连接(一旦连接就会出现上面的第二种情况),所以最后4个点只有8种可能性,将所有点的都试一遍,得出如下结论(经过几轮测试,基本结论如下):

  1. 还有一个关于前后摄像头的切换的问题,我上面的做法是在点击切换摄像头操作的时候只针对摄像头进行了释放重启操作,直接在onDrawFrame方法中对渲染矩阵进行了修改,没有对SurfaceTexture进行数据清除(具体看上面代码)。然而也看了一些主流的第三方Demo,这里不列出名字了。他们的做法是摄像头和surfaceTexture一块释放。当然,两种方式都可以,我上面的那种方式暂时没找到什么问题,而且我通过实测比第二种方式看到相机数据的时间要快一点。

运行效果

5. 总结

弄清楚没个步骤之后你就会发现之前感觉无从下手的东西也就这样。没什么大不了的。

個人的な提案:カメラプロジェクトを行うときは、各ステップを明確にするのが最善であり、明確なロジックは多くの時間を節約します。最初は誰かのデモを撮ってこの部分をやっていたのですが、間違えたときは始められませんでした。解決策を探すために百度に行きました。多くの時間を無駄にしました。理由はわかりません。再編成した後、あなたは多くを学ぶでしょう。

最後に、これ以上は言わないでください。わからないことがあれば、私にメッセージを残すか、同じ名前のaserbaoのWeChatアカウントをフォローして、個人的にチャットしてください。よく書かれていると思うなら、親指を立ててください。忘れてしまった、ソースコードと注意があります、階下を見てください。

6.ソースリンク

AndroidCameraが便利だと思ったら、星を付けてください

ナゲッツテクノロジーコミュニティのクリエイター署名プログラムの募集に参加しています。リンクをクリックして登録し、送信してください

おすすめ

転載: juejin.im/post/7117202297569935397