欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android使用OpenGL和MediaCodec錄制功能

 更新時間:2025年04月07日 10:29:52   作者:布拉德很帥  
OpenGL是一個跨平臺的操作GPU的API,但OpenGL需要本地視窗系統(tǒng)進行交互,這就需要一個中間控制層, EGL就是連接OpenGL ES和本地窗口系統(tǒng)的接口,引入EGL就是為了屏蔽不同平臺上的區(qū)別,這篇文章主要介紹了Android使用OpenGL和MediaCodec錄制功能,需要的朋友可以參考下

一,什么是opengl

Open Graphics Library     圖形領(lǐng)域的工業(yè)標準,是一套跨編程語言、跨平臺的、專業(yè)的圖形編程(軟件)接口。它用于二維、三維圖像,是一個功能強大,調(diào)用方便的底層圖形庫。     

與硬件無關(guān)??梢栽诓煌钠脚_如Windows、Linux、Mac、Android、IOS之間進行移植。因此,支持OpenGL的軟件具有很好的移植性,可以獲得非常廣泛的應(yīng)用。

OpenGL ES 1.0 和 1.1 :Android 1.0和更高的版本支持這個API規(guī)范。 OpenGL ES 2.0 :Android 2.2(API 8)和更高的版本支持這個API規(guī)范。 OpenGL ES 3.0 :Android 4.3(API 18)和更高的版本支持這個API規(guī)范。 OpenGL ES 3.1 : Android 5.0(API 21)和更高的版本支持這個API規(guī)范

還須要由設(shè)備制造商提供了實現(xiàn)支持 目前廣泛支持的是2.0 <uses-feature android:glEsVersion="0x00020000" android:required="true"/>

二,什么是Android OpenGL ES

針對手機、PDA和游戲主機等嵌入式設(shè)備而設(shè)計的OpenGL API 子集。

GLSurfaceView

繼承至SurfaceView,它內(nèi)嵌的surface專門負責OpenGL渲染。     管理Surface與EGL     允許自定義渲染器(render)。     讓渲染器在獨立的線程里運作,和UI線程分離。      支持按需渲染(on-demand)和連續(xù)渲染(continuous)。

OpenGL是一個跨平臺的操作GPU的API,但OpenGL需要本地視窗系統(tǒng)進行交互,這就需要一個中間控制層, EGL就是連接OpenGL ES和本地窗口系統(tǒng)的接口,引入EGL就是為了屏蔽不同平臺上的區(qū)別。

三, OpenGL 繪制流程

其實就是一個可編程管線pipline

渲染管線做的事情就是讓計算機完成圖形功能。 固定管線:程序員只能設(shè)置參數(shù)。比如 f(x)=axx + bx + c程序員只能設(shè)置a,b,c的值,卻不能修改這個公式。 可編程管線:程序員掌控一切。

四, OpenGL坐標系

五, OpenGL 著色器

著色器(Shader)是運行在GPU上的小程序。頂點著色器(vertex shader)  如何處理頂點、法線等數(shù)據(jù)的小程序。片元著色器(fragment shader)     如何處理光、陰影、遮擋、環(huán)境等等對物體表面的影響,最終生成一副圖像的小程序

六, GLSL編程語言

七,使用MediaCodec錄制在Opengl中渲染架構(gòu)

八,代碼實現(xiàn)

8.1 自定義渲染view繼承GLSurfaceView

public class TigerView extends GLSurfaceView {
    private TigerRender mTigerRender;
    //默認正常速度
    private Speed mSpeed = Speed.MODE_NORMAL;
    public void setSpeed(Speed speed) {
        mSpeed = speed;
    }
    public enum Speed {
        MODE_EXTRA_SLOW, MODE_SLOW, MODE_NORMAL, MODE_FAST, MODE_EXTRA_FAST
    }
    public TigerView(Context context) {
        super(context);
    }
    public TigerView(Context context, AttributeSet attrs) {
        super(context, attrs);
        /**
         * 設(shè)置egl版本
         */
        setEGLContextClientVersion(2);
        /**
         * 設(shè)置渲染器
         */
        mTigerRender = new TigerRender(this);
        setRenderer(mTigerRender);
        /**
         * 設(shè)置按需渲染,當我們調(diào)用requestRender()的時候就會調(diào)用GlThread回調(diào)一次onDrawFrame()
         */
        setRenderMode(RENDERMODE_WHEN_DIRTY);
    }
    public void startRecord() {
        float speed = 1.f;
        switch (mSpeed) {
            case MODE_EXTRA_SLOW:
                speed = 0.3f;
                break;
            case MODE_SLOW:
                speed = 0.5f;
                break;
            case MODE_NORMAL:
                speed = 1.f;
                break;
            case MODE_FAST:
                speed = 1.5f;
                break;
            case MODE_EXTRA_FAST:
                speed = 3.f;
                break;
        }
        mTigerRender.startRecord(speed);
    }
    public void stopRecord() {
        mTigerRender.stopRecord();
    }
}

8.2 自定義渲染器TigerRender

public class TigerRender implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener {
    private final TigerView mView;
    private CameraHelper mCameraHelper;
    private SurfaceTexture mSurfaceTexture;
    float[] mtx = new float[16];
    private ScreeFilter mScreeFilter;
    private int[] mTextures;
    private CameraFilter mCameraFilter;
    private MediaRecorder mMediaRecorder;
    public TigerRender(TigerView tigerView) {
        mView = tigerView;
    }
    /**
     * 畫布創(chuàng)建好了
     *
     * @param gl     the GL interface. Use <code>instanceof</code> to
     *               test if the interface supports GL11 or higher interfaces.
     * @param config the EGLConfig of the created surface. Can be used
     *               to create matching pbuffers.
     */
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        mCameraHelper = new CameraHelper(Camera.CameraInfo.CAMERA_FACING_BACK);
        //準備好攝像頭繪制的畫布
        //通過open gl創(chuàng)建一個紋理id
        mTextures = new int[1];
        GLES20.glGenTextures(mTextures.length, mTextures, 0);
        mSurfaceTexture = new SurfaceTexture(mTextures[0]);
        //設(shè)置有一幀新的數(shù)據(jù)到來的時候,回調(diào)監(jiān)聽
        mSurfaceTexture.setOnFrameAvailableListener(this);
        //必須要在GlThread里面創(chuàng)建著色器程序
        mCameraFilter = new CameraFilter(mView.getContext());
        mScreeFilter = new ScreeFilter(mView.getContext());
        EGLContext eglContext = EGL14.eglGetCurrentContext();
        mMediaRecorder = new MediaRecorder(mView.getContext(), "/mnt/sdcard/test.mp4", CameraHelper.HEIGHT, CameraHelper.WIDTH, eglContext);
    }
    /**
     * 畫布發(fā)生改變
     *
     * @param gl     the GL interface. Use <code>instanceof</code> to
     *               test if the interface supports GL11 or higher interfaces.
     * @param width
     * @param height
     */
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        mCameraHelper.startPreview(mSurfaceTexture);
        mCameraFilter.onReady(width, height);
        mScreeFilter.onReady(width, height);
    }
    /**
     * 畫畫
     *
     * @param gl the GL interface. Use <code>instanceof</code> to
     *           test if the interface supports GL11 or higher interfaces.
     */
    @Override
    public void onDrawFrame(GL10 gl) {
        //告訴open gl需要把屏幕清理成 什么樣子的顏色
        GLES20.glClearColor(0, 0, 0, 0);
        //開始真正的屏幕顏色清理,也就是上一次設(shè)置的屏幕顏色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        //把攝像頭采集的數(shù)據(jù)輸出出來
        //更新紋理,然后我們才可以使用opengl從SurfaceTexure當中獲取數(shù)據(jù),進行渲染
        mSurfaceTexture.updateTexImage();
        //mSurfaceTexture比較特殊,在設(shè)置坐標的時候,需要一個變換矩陣,使用的是特殊的采樣器samplerExternalOES
        //這種采樣器,正常的是sample2D
        mSurfaceTexture.getTransformMatrix(mtx);
        mCameraFilter.setMatrix(mtx);
        int id = mCameraFilter.onDrawFrame(mTextures[0]);
        //在這里添加各種效果,相當于責任鏈
        //開始畫畫
        mScreeFilter.onDrawFrame(id);
        mMediaRecorder.encodeFrame(id, mSurfaceTexture.getTimestamp());
    }
    /**
     * SurfaceTexture有一個新的有效的圖片的時候會被回調(diào),此時可以把這個數(shù)據(jù)回調(diào)給GLSurfaceView的onDrawFrame
     *
     * @param surfaceTexture
     */
    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        if (mView != null) {
            //開始渲染,有一幀新的圖像,就開始調(diào)用GLSurfaceView的onDrawFrame進行繪制
            mView.requestRender();
        }
    }
    public void startRecord(float speed) {
        try {
            mMediaRecorder.start(speed);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public void stopRecord() {
        mMediaRecorder.stop();
    }
}

8.3 創(chuàng)建編碼器MediaRecorder

/**
 * 視頻錄制
 */
public class MediaRecorder {
    private final Context mContext;
    private final String mPath;
    private final int mWidth;
    private final int mHeight;
    private final EGLContext mEglContext;
    private MediaCodec mMediaCodec;
    private Surface mInputSurface;
    private MediaMuxer mMediaMuxer;
    private Handler mHandler;
    private EGLBase mEglBase;
    private boolean isStart;
    private int index;
    private float mSpeed;
    /**
     * @param context
     * @param path    視頻保存地址
     * @param width   視頻寬
     * @param height  視頻高
     */
    public MediaRecorder(Context context, String path, int width, int height, EGLContext eglContext) {
        mContext = context.getApplicationContext();
        mPath = path;
        mWidth = width;
        mHeight = height;
        mEglContext = eglContext;
    }
    /**
     * 開始錄制視頻
     */
    public void start(float speed) throws IOException {
        /**
         * 配置MediaCodec編碼器,視頻編碼的寬,高,幀率,碼率
         * 錄制成mp4格式,視頻編碼格式是h264 MIMETYPE_VIDEO_AVC 高級編碼
         */
        mSpeed = speed;
        MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);
        //配置碼率 1500kbs
        videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, 1500_000);
        //幀率
        videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 20);
        //關(guān)鍵字間隔
        videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 20);
        //創(chuàng)建視頻高級編碼器
        mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
        //因為是從Surface中讀取的,所以不需要設(shè)置這個顏色格式
        videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        //配置編碼器,CONFIGURE_FLAG_ENCODE,
        mMediaCodec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        //交給虛擬屏幕,通過opengl 將預(yù)覽的紋理繪制到這一個虛擬屏幕中,這樣子Mediacodc 就會自動編碼這一幀圖像
        mInputSurface = mMediaCodec.createInputSurface();
        //mp4 播放流程  解復(fù)用--》解碼 》繪制
        //mp4 編碼流程  封裝器--》編碼
        //MUXER_OUTPUT_MPEG_4 MP4格式封裝器,將h.264通過他寫出到文件就可以了
        mMediaMuxer = new MediaMuxer(mPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        /**
         * 配置EGL環(huán)境,也就是配置我們的虛擬屏幕環(huán)境
         */
        HandlerThread handlerThread = new HandlerThread("ViewoCodec");
        handlerThread.start();
        Looper looper = handlerThread.getLooper();
        //子線程和子線程之間的通信
        mHandler = new Handler(looper);
        //EGl的綁定線程,對我們自己創(chuàng)建的EGl環(huán)境,都是在這個線程里面進行
        mHandler.post(() -> {
            //創(chuàng)建我們的EGL環(huán)境(虛擬設(shè)備,EGL上下文 )
            mEglBase = new EGLBase(mContext, mWidth, mHeight, mInputSurface, mEglContext);
            //啟動編碼器
            mMediaCodec.start();
            isStart = true;
        });
    }
    /**
     * textureId 紋理id
     * 調(diào)用一次,就有一個新的圖片需要編碼
     */
    public void encodeFrame(int textureId, long timesnap) {
        if (!isStart) {
            return;
        }
        //切換到子線程中編碼
        mHandler.post(() -> {
            //把圖像紋理畫到虛擬屏幕里面
            mEglBase.draw(textureId, timesnap);
            //此時我們需要從編碼器里面的輸出緩沖區(qū)獲取編碼以后的數(shù)據(jù)就可以了,
            getCodec(false);
        });
    }
    private void getCodec(boolean endOfStream) {
        if (endOfStream) {
            //表示停止錄制,此時我們不錄制了,需要給mediacoic 通知
            mMediaCodec.signalEndOfInputStream();
        }
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        //希望將已經(jīng)編碼完成的數(shù)據(jù)都獲取到,然后寫出到mp4文件中
        while (true) {
            //并不是傳給mediacodec一幀數(shù)據(jù)就表示可以編碼出一幀數(shù)據(jù),有可能需要好多幀數(shù)據(jù)才可以同時編碼出數(shù)據(jù)出來
            //輸出緩沖區(qū)
            //傳遞-1表示一直等到輸出緩沖區(qū)有一個編碼好的有效的數(shù)據(jù)以后才會繼續(xù)向下走,不然就會一直卡在127行,
            //10_000超時時間
            int status = mMediaCodec.dequeueOutputBuffer(bufferInfo, 10_000);
            if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {
                //如果是停止,就繼續(xù)循環(huán),
                // 繼續(xù)循環(huán)就表示不會接收到新的等待編碼的圖像了
                //相當于保證mediacodic中所有待編碼的數(shù)據(jù)都編碼完成
                // 標記不是停止,我們退出,下一輪接收到更多的數(shù)據(jù)才來輸出編碼以后的數(shù)據(jù),我們就讓繼續(xù)走
                // 表示需要更多數(shù)據(jù)才可以編碼出圖像  false是繼續(xù)錄制,未來還有機會在調(diào)用getCodec
                if (!endOfStream) {
                    //結(jié)束錄制了
                    break;
                }
                //否則繼續(xù)
            } else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                //開始編碼,就會調(diào)用一次
                MediaFormat outputFormat = mMediaCodec.getOutputFormat();
                //配置封裝器,增加一路指定格式的媒體流
                index = mMediaMuxer.addTrack(outputFormat);
                //啟動封裝器
                mMediaMuxer.start();
            } else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                //忽略
            } else {
                //成功取出一個有效的輸出
                ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(status);
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    //如果獲取的ByteBuffer是配置信息,那么就不需要寫出到mp4文件中
                    bufferInfo.size = 0;
                }
                if (bufferInfo.size != 0) {
                    bufferInfo.presentationTimeUs = (long) (bufferInfo.presentationTimeUs / mSpeed);
                    //寫出到 mp4文件中
                    //根據(jù)偏移定位去獲取數(shù)據(jù),而不是從0開始
                    outputBuffer.position(bufferInfo.offset);
                    //設(shè)置可讀可寫的總長度
                    outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
                    mMediaMuxer.writeSampleData(index, outputBuffer, bufferInfo);
                }
                //輸出緩沖區(qū)使用完畢了, 此時就可以回收了,讓mediacodec繼續(xù)使用
                mMediaCodec.releaseOutputBuffer(status, false);
                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    //結(jié)束了,
                    break;
                }
            }
        }
    }
    public void stop() {
        isStart = false;
        mHandler.post(() -> {
            getCodec(true);
            mMediaCodec.stop();
            mMediaCodec.release();
            mMediaCodec = null;
            mMediaMuxer.stop();
            mMediaMuxer.release();
            mMediaMuxer = null;
            mEglBase.release();
            mEglBase = null;
            mInputSurface = null;
            mHandler.getLooper().quitSafely();
            mHandler = null;
        });
    }
}

8.4 配置egl環(huán)境

/**
 * EGL配置 和錄制opengl的操作
 */
public class EGLBase {
    private final EGLSurface mEglSurface;
    private final ScreeFilter mScreeFilter;
    private EGLDisplay mEglDisplay;
    private EGLConfig mEglConfig;
    private EGLContext mEGLContext;
    /**
     * @param context
     * @param width
     * @param height
     * @param surface MediaCodec創(chuàng)建的surface, 我們需要將這個surface貼到虛擬屏幕里面
     */
    public EGLBase(Context context, int width, int height, Surface surface, EGLContext eglContext) {
        createEGL(eglContext);
        //把surface貼到EGLDisplay 虛擬屏幕里面
        int[] attrib_list = {
                //不需要配置什么屬性
                EGL14.EGL_NONE};
        //就是向mEglDisplay這個虛擬屏幕上面畫畫
        mEglSurface = EGL14.eglCreateWindowSurface(mEglDisplay, mEglConfig, surface, attrib_list, 0);
        //必須要綁定當前線程的顯示上下文,不然就繪制不上去,這樣子之后操作的opelgl就是在這個虛擬屏幕上操作,讀和寫都是在同一個surface里面
        if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEGLContext)) {
            throw new RuntimeException("eglMakeCurrent failed");
        }
        //向虛擬屏幕畫畫
        mScreeFilter = new ScreeFilter(context);
        mScreeFilter.onReady(width, height);
    }
    private void createEGL(EGLContext eglContext) {
        //創(chuàng)建虛擬顯示器
        mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay failed");
        }
        int[] version = new int[2];
        //初始化虛擬設(shè)備
        if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)) {
            throw new RuntimeException("eglInitialize failed");
        }
        int[] attrib_list = new int[]{//rgba 紅綠藍透明度
                EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8, EGL14.EGL_ALPHA_SIZE, 8, EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,//和egl的版本有關(guān)系
                EGL14.EGL_NONE//這個很重要,一定要配置為NONE,表示配置結(jié)束了
        };
        EGLConfig[] configs = new EGLConfig[1];
        int[] num_config = new int[1];
        boolean eglChooseConfig = EGL14.eglChooseConfig(mEglDisplay, attrib_list, 0, configs, 0, configs.length, num_config, 0);
        if (!eglChooseConfig) {
            //如果配置失敗
            throw new IllegalArgumentException("eglChooseConfig failed");
        }
        mEglConfig = configs[0];
        int[] attriblist = {EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE};
        //創(chuàng)建EGL上下文
        //share_context 共享上下,傳遞繪制線程的(GLThread)EGL上下文,達到共享資源的目的
        mEGLContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, eglContext, attriblist, 0);
        if (mEGLContext == null || mEGLContext == EGL14.EGL_NO_CONTEXT) {
            mEGLContext = null;
            throw new RuntimeException("createContex error !");
        }
    }
    /**
     * @param textureId 紋理id,代表一張圖片
     * @param timesnap  時間戳
     */
    public void draw(int textureId, long timesnap) {
        //必須要綁定當前線程的顯示上下文,不然就繪制不上去,這樣子之后操作的opelgl就是在這個虛擬屏幕上操作,讀和寫都是在同一個surface里面
        //畫畫之前也必須要綁定
        if (!EGL14.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEGLContext)) {
            throw new RuntimeException("eglMakeCurrent failed");
        }
        //向虛擬屏幕畫畫
        mScreeFilter.onDrawFrame(textureId);
        //刷新eglSurface時間戳
        EGLExt.eglPresentationTimeANDROID(mEglDisplay, mEglSurface, timesnap);
        //交換數(shù)據(jù),EGL工作模式,雙緩存模式,內(nèi)部有兩個frameBuff,當EGL將一個frame顯示到屏幕上以后,
        // 另一個frame就在后臺等待opengl進行交換
        //也就是畫完一次,交換一次
        EGL14.eglSwapBuffers(mEglDisplay, mEglSurface);
    }
    public void release() {
        EGL14.eglDestroySurface(mEglDisplay, mEglSurface);
        EGL14.eglMakeCurrent(mEglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
        EGL14.eglDestroyContext(mEglDisplay, mEGLContext);
        EGL14.eglReleaseThread();
        EGL14.eglTerminate(mEglDisplay);
    }
}

8.5 配置shader

public class AbstractFilter {
    protected FloatBuffer mGLVertexBuffer;
    protected FloatBuffer mGLTextureBuffer;
    //頂點著色
    protected int mVertexShaderId;
    //片段著色
    protected int mFragmentShaderId;
    protected int mGLProgramId;
    /**
     * 頂點著色器
     * attribute vec4 position;
     * 賦值給gl_Position(頂點)
     */
    protected int vPosition;
    /**
     * varying vec2 textureCoordinate;
     */
    protected int vCoord;
    /**
     * uniform mat4 vMatrix;
     */
    protected int vMatrix;
    /**
     * 片元著色器
     * Samlpe2D 擴展 samplerExternalOES
     */
    protected int vTexture;
    protected int mOutputWidth;
    protected int mOutputHeight;
    public AbstractFilter(Context context, int vertexShaderId, int fragmentShaderId) {
        this.mVertexShaderId = vertexShaderId;
        this.mFragmentShaderId = fragmentShaderId;
        // 4個點 x,y = 4*2 float 4字節(jié) 所以 4*2*4
        mGLVertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mGLVertexBuffer.clear();
        float[] VERTEX = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
        mGLVertexBuffer.put(VERTEX);
        mGLTextureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mGLTextureBuffer.clear();
        float[] TEXTURE = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};
        mGLTextureBuffer.put(TEXTURE);
        initilize(context);
        initCoordinate();
    }
    protected void initilize(Context context) {
        String vertexSharder = OpenGLUtils.readRawTextFile(context, mVertexShaderId);
        String framentShader = OpenGLUtils.readRawTextFile(context, mFragmentShaderId);
        mGLProgramId = OpenGLUtils.loadProgram(vertexSharder, framentShader);
        // 獲得著色器中的 attribute 變量 position 的索引值
        vPosition = GLES20.glGetAttribLocation(mGLProgramId, "vPosition");
        vCoord = GLES20.glGetAttribLocation(mGLProgramId, "vCoord");
        vMatrix = GLES20.glGetUniformLocation(mGLProgramId, "vMatrix");
        // 獲得Uniform變量的索引值
        vTexture = GLES20.glGetUniformLocation(mGLProgramId, "vTexture");
    }
    public void onReady(int width, int height) {
        mOutputWidth = width;
        mOutputHeight = height;
    }
    public void release() {
        GLES20.glDeleteProgram(mGLProgramId);
    }
    public int onDrawFrame(int textureId) {
        //設(shè)置顯示窗口
        GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight);
        //使用著色器
        GLES20.glUseProgram(mGLProgramId);
        //傳遞坐標
        mGLVertexBuffer.position(0);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGLVertexBuffer);
        GLES20.glEnableVertexAttribArray(vPosition);
        mGLTextureBuffer.position(0);
        GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGLTextureBuffer);
        GLES20.glEnableVertexAttribArray(vCoord);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        //綁定
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        GLES20.glUniform1i(vTexture, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        //解綁
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        return textureId;
    }
    //修改坐標
    protected void initCoordinate() {
    }
}

到此這篇關(guān)于Android使用OpenGL和MediaCodec錄制的文章就介紹到這了,更多相關(guān)Android使用OpenGL和MediaCodec內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Flutter實現(xiàn)心動的動畫特效

    Flutter實現(xiàn)心動的動畫特效

    為了追求更好的用戶體驗,有時候我們需要一個類似心跳一樣跳動著的控件來吸引用戶的注意力。本文將利用Flutter實現(xiàn)這一動畫特效,需要的可以參考一下
    2022-04-04
  • Android編程之界面跳動提示動畫效果實現(xiàn)方法

    Android編程之界面跳動提示動畫效果實現(xiàn)方法

    這篇文章主要介紹了Android編程之界面跳動提示動畫效果實現(xiàn)方法,實例分析了Android動畫效果的布局及功能相關(guān)實現(xiàn)技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-11-11
  • 解決Android Studio 格式化 Format代碼快捷鍵問題

    解決Android Studio 格式化 Format代碼快捷鍵問題

    這篇文章主要介紹了解決Android Studio 格式化 Format代碼快捷鍵問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-03-03
  • Android Bitmap壓縮方式分析

    Android Bitmap壓縮方式分析

    這篇文章主要介紹了Android Bitmap壓縮方式分析的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • android 顯示gif圖片實例詳解

    android 顯示gif圖片實例詳解

    本文主要介紹android 顯示gif圖片的知識,這里整理相關(guān)資料及簡單實例代碼,有需要的小伙伴可以參考下
    2016-09-09
  • Android Studio 合并module到統(tǒng)一文件夾的方法

    Android Studio 合并module到統(tǒng)一文件夾的方法

    這篇文章主要介紹了Android Studio 合并module到統(tǒng)一文件夾的方法,補充介紹了android studio關(guān)于同名資源文件的合并技巧,需要的朋友可以參考下
    2018-04-04
  • Android的八種對話框的實現(xiàn)代碼示例

    Android的八種對話框的實現(xiàn)代碼示例

    本篇文章主要介紹了Android的八種對話框的實現(xiàn)代碼示例,這里整理了詳細的代碼,非常具有實用價值,有需要的小伙伴可以參考下。
    2017-09-09
  • 設(shè)置Android系統(tǒng)永不鎖屏永不休眠的方法

    設(shè)置Android系統(tǒng)永不鎖屏永不休眠的方法

    在進行Android系統(tǒng)開發(fā)的時候,有些特定的情況需要設(shè)置系統(tǒng)永不鎖屏,永不休眠。本篇文章給大家介紹Android 永不鎖屏,開機不鎖屏,刪除設(shè)置中休眠時間選項,需要的朋友一起學(xué)習吧
    2016-03-03
  • Android開發(fā)之TimePicker控件用法實例詳解

    Android開發(fā)之TimePicker控件用法實例詳解

    這篇文章主要介紹了Android開發(fā)之TimePicker控件用法,結(jié)合實例形式詳細分析了Android項目的建立及TimePicker控件的具體使用技巧,需要的朋友可以參考下
    2016-02-02
  • Android 獲取藍牙Mac地址的正確方法

    Android 獲取藍牙Mac地址的正確方法

    android 從6.0開始,通過BluetoothAdapter.getDefaultAdapter().getAddress()獲取的地址是一個固定值02:00:00:00:00:00。下面給大家介紹Android 獲取藍牙Mac地址的正確方法,一起看看吧
    2017-12-12

最新評論