Camera中通過startPreview + SurfaceTexture + Opengl ES實(shí)現(xiàn)簡單渲染

今天剛好是2016-09-30,假期前最后一天,上班提不起多大的精神,不想起馬上可以回家可以好好休息七天了,心里還是挺激動的。趁現(xiàn)在有點(diǎn)時(shí)間,想把最近整理出來的一個(gè)小demo分享出來,主要是通過Camera預(yù)覽功能采集數(shù)據(jù),然后通過opengl進(jìn)行渲染,最后保存至Mp4文件中。主要用了四個(gè)類實(shí)現(xiàn)此功能:SurfaceTextureRender、SurfaceTextureWrapper、WindowInputSurface、EncodeManager,下面分別會對這個(gè)類進(jìn)行詳細(xì)的講解。
1、SurfaceTextureRender,主要是自定義渲染render,源碼如下:

/
*
* Created by jsp on 2016/9/29.
*/

public classSurfaceTextureRender {

private static finalStringTAG="SurfaceTextureRender";

private static final intFLOAT_SIZE_BYTES=4;

private static final intTRIANGLE_VERTICES_DATA_STRIDE_BYTES=5*FLOAT_SIZE_BYTES;

private static final intTRIANGLE_VERTICES_DATA_POS_OFFSET=0;

private static final intTRIANGLE_VERTICES_DATA_UV_OFFSET=3;

private final float[]mTriangleVerticesData= {

// X, Y, Z, U, V

-1.0f,-1.0f,0,0.f,0.f,

1.0f,-1.0f,0,1.f,0.f,

-1.0f,1.0f,0,0.f,1.f,

1.0f,1.0f,0,1.f,1.f,

};

privateFloatBuffermTriangleVertices;

private static finalStringVERTEX_SHADER=

"uniform mat4 uMVPMatrix;\n"+

"uniform mat4 uSTMatrix;\n"+

"attribute vec4 aPosition;\n"+

"attribute vec4 aTextureCoord;\n"+

"varying vec2 vTextureCoord;\n"+

"void main() {\n"+

"    gl_Position = uMVPMatrix * aPosition;\n"+

"    vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n"+

"}\n";

//fragment shader

private static finalStringFRAGMENT_SHADER=

"#extension GL_OES_EGL_image_external : require\n"+

"precision mediump float;\n"+// highp here doesn't seem to matter

"varying vec2 vTextureCoord;\n"+

"uniform samplerExternalOES sTexture;\n"+

"void main() {\n"+

"    gl_FragColor = texture2D(sTexture, vTextureCoord);\n"+

"}\n";

//交換改變fragment shader

public static finalStringSWAPPED_FRAGMENT_SHADER=

"#extension GL_OES_EGL_image_external : require\n"+

"precision mediump float;\n"+

"varying vec2 vTextureCoord;\n"+

"uniform samplerExternalOES sTexture;\n"+

"void main() {\n"+

"  gl_FragColor = texture2D(sTexture, vTextureCoord).gbra;\n"+

"}\n";

private float[]mMVPMatrix=new float[16];

private float[]mSTMatrix=new float[16];

private intmProgram;

private intmTextureID= -12345;

private intmuMVPMatrixHandle;

private intmuSTMatrixHandle;

private intmaPositionHandle;

private intmaTextureHandle;

publicSurfaceTextureRender() {

mTriangleVertices= ByteBuffer.allocateDirect(

mTriangleVerticesData.length*FLOAT_SIZE_BYTES)

.order(ByteOrder.nativeOrder()).asFloatBuffer();

mTriangleVertices.put(mTriangleVerticesData).position(0);

Matrix.setIdentityM(mSTMatrix,0);

}

/**

*返回紋理id

*@return

*/

public final intgetTextureId() {

returnmTextureID;

}

/**

*開始渲染

*@paramst

*/

public final void drawFrame(SurfaceTexture st) {

checkGlError("onDrawFrame start");

st.getTransformMatrix(mSTMatrix);

// (optional) clear to green so we can see if we're failing to set pixels

GLES20.glClearColor(0.0f,1.0f,0.0f,1.0f);

GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT| GLES20.GL_COLOR_BUFFER_BIT);

GLES20.glUseProgram(mProgram);

checkGlError("glUseProgram");

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,mTextureID);

mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);

GLES20.glVertexAttribPointer(maPositionHandle,3,GLES20.GL_FLOAT, false,

TRIANGLE_VERTICES_DATA_STRIDE_BYTES,mTriangleVertices);

checkGlError("glVertexAttribPointer maPosition");

GLES20.glEnableVertexAttribArray(maPositionHandle);

checkGlError("glEnableVertexAttribArray maPositionHandle");

mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);

GLES20.glVertexAttribPointer(maTextureHandle,2,GLES20.GL_FLOAT, false,

TRIANGLE_VERTICES_DATA_STRIDE_BYTES,mTriangleVertices);

checkGlError("glVertexAttribPointer maTextureHandle");

GLES20.glEnableVertexAttribArray(maTextureHandle);

checkGlError("glEnableVertexAttribArray maTextureHandle");

Matrix.setIdentityM(mMVPMatrix,0);

GLES20.glUniformMatrix4fv(muMVPMatrixHandle,1, false,mMVPMatrix,0);

GLES20.glUniformMatrix4fv(muSTMatrixHandle,1, false,mSTMatrix,0);

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);

checkGlError("glDrawArrays");

// IMPORTANT: on some devices, if you are sharing the external texture between two

// contexts, one context may not see updates to the texture unless you un-bind and

// re-bind it.  If you're not using shared EGL contexts, you don't need to bind

// texture 0 here.

GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,0);

}

/**

*初始化gl資源

*/

public final voidsurfaceCreated() {

mProgram= createProgram(VERTEX_SHADER,FRAGMENT_SHADER);

if(mProgram==0) {

throw newRuntimeException("failed creating program");

}

maPositionHandle= GLES20.glGetAttribLocation(mProgram,"aPosition");

checkLocation(maPositionHandle,"aPosition");

maTextureHandle= GLES20.glGetAttribLocation(mProgram,"aTextureCoord");

checkLocation(maTextureHandle,"aTextureCoord");

muMVPMatrixHandle= GLES20.glGetUniformLocation(mProgram,"uMVPMatrix");

checkLocation(muMVPMatrixHandle,"uMVPMatrix");

muSTMatrixHandle= GLES20.glGetUniformLocation(mProgram,"uSTMatrix");

checkLocation(muSTMatrixHandle,"uSTMatrix");

int[] textures =new int[1];

GLES20.glGenTextures(1,textures,0);

mTextureID= textures[0];

GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,mTextureID);

checkGlError("glBindTexture mTextureID");

GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_MIN_FILTER,

GLES20.GL_NEAREST);

GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_MAG_FILTER,

GLES20.GL_LINEAR);

GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_WRAP_S,

GLES20.GL_CLAMP_TO_EDGE);

GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES20.GL_TEXTURE_WRAP_T,

GLES20.GL_CLAMP_TO_EDGE);

checkGlError("glTexParameter");

}

/**

*重新修改Fragment Shader

*@paramfragmentShader

*/

public final voidchangeFragmentShader(String fragmentShader) {

if(fragmentShader ==null) {

fragmentShader =FRAGMENT_SHADER;

}

GLES20.glDeleteProgram(mProgram);

mProgram= createProgram(VERTEX_SHADER,fragmentShader);

if(mProgram==0) {

throw newRuntimeException("failed creating program");

}

}

/**

*設(shè)置shader

*@paramshaderType

*@paramsource

*@return

*/

private intloadShader(intshaderType,String source) {

intshader = GLES20.glCreateShader(shaderType);

checkGlError("glCreateShader type="+ shaderType);

GLES20.glShaderSource(shader,source);

GLES20.glCompileShader(shader);

int[] compiled =new int[1];

GLES20.glGetShaderiv(shader,GLES20.GL_COMPILE_STATUS,compiled,0);

if(compiled[0] ==0) {

Log.e(TAG,"Could not compile shader "+ shaderType +":");

Log.e(TAG," "+ GLES20.glGetShaderInfoLog(shader));

GLES20.glDeleteShader(shader);

shader =0;

}

returnshader;

}

/**

*創(chuàng)建gl program

*@paramvertexSource

*@paramfragmentSource

*@return

*/

private intcreateProgram(String vertexSource,String fragmentSource) {

intvertexShader = loadShader(GLES20.GL_VERTEX_SHADER,vertexSource);

if(vertexShader ==0) {

return0;

}

intpixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER,fragmentSource);

if(pixelShader ==0) {

return0;

}

intprogram = GLES20.glCreateProgram();

if(program ==0) {

Log.e(TAG,"Could not create program");

}

GLES20.glAttachShader(program,vertexShader);

checkGlError("glAttachShader");

GLES20.glAttachShader(program,pixelShader);

checkGlError("glAttachShader");

GLES20.glLinkProgram(program);

int[] linkStatus =new int[1];

GLES20.glGetProgramiv(program,GLES20.GL_LINK_STATUS,linkStatus,0);

if(linkStatus[0] != GLES20.GL_TRUE) {

Log.e(TAG,"Could not link program: ");

Log.e(TAG,GLES20.glGetProgramInfoLog(program));

GLES20.glDeleteProgram(program);

program =0;

}

returnprogram;

}

public voidcheckGlError(String op) {

interror;

while((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {

Log.e(TAG,op +": glError "+ error);

throw newRuntimeException(op +": glError "+ error);

}

}

public final voidcheckLocation(intlocation,String label) {

    if(location <0) {

        throw newRuntimeException("Unable to locate '"+ label +"' in program");

    }
  }

}

2、SurfaceTextureWrapper,主要是用作自定義紋理與Camera取出來的數(shù)據(jù)進(jìn)行包裝;

/**
 * Created by jsp on 2016/9/29.
 */

public class SurfaceTextureWrapper
        implements SurfaceTexture.OnFrameAvailableListener{
    private SurfaceTexture  mSurfaceTexture;
    private SurfaceTextureRender mESTextureRender;

    private Object mSyncObject = new Object();

    private boolean mFrameAvailable;

    public SurfaceTextureWrapper(){
        mESTextureRender       = new SurfaceTextureRender();
        mESTextureRender.surfaceCreated();

        //自定義texture地址直接構(gòu)造給SurfaceTexture
        mSurfaceTexture        = new SurfaceTexture(
                mESTextureRender.getTextureId());

        mSurfaceTexture.setOnFrameAvailableListener(this);

    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        synchronized (mSyncObject) {
            if (mFrameAvailable) {
                throw new RuntimeException("mFrameAvailable already set, frame could be dropped");
            }
            mFrameAvailable = true;
            mSyncObject.notifyAll();
        }
    }

    /**
     * 渲染
     */
    public final void drawImage() {
        mESTextureRender.drawFrame(mSurfaceTexture);
    }

    /**
     * 同步等待下一幀數(shù)據(jù)
     */
    public final void awaitNewImage() {
//        final int TIMEOUT_MS = 2500;
        synchronized (mSyncObject) {
            while (!mFrameAvailable) {
                try {
                    // Wait for onFrameAvailable() to signal us.  Use a timeout to avoid
                    // stalling the test if it doesn't arrive.
//                    mSyncObject.wait(TIMEOUT_MS);
                    mSyncObject.wait();
                    if (!mFrameAvailable) {
                        // TODO: if "spurious wakeup", continue while loop
                        throw new RuntimeException("Camera frame wait timed out");
                    }
                } catch (InterruptedException ie) {
                    throw new RuntimeException(ie);
                }
            }
            mFrameAvailable = false;
        }
        // Latch the data.
        mESTextureRender.checkGlError("before updateTexImage");
        //更新數(shù)據(jù)
        mSurfaceTexture.updateTexImage();
    }

    /**
     * 獲取渲染后SurfaceTexture
     * @return
     */
    public final SurfaceTexture getSurfaceTexture(){
        return mSurfaceTexture;
    }

    /**
     * 修改Fragment Shader
     * @param fragmentShader
     */
    public final void changeFragmentShader(String fragmentShader) {
        mESTextureRender.changeFragmentShader(fragmentShader);
    }

    /**
     * 釋放資源
     */
    public final void release(){
        mSurfaceTexture = null;
        mESTextureRender= null;
    }

}
3、WindowInputSurface EGL包裝類,里面封裝了,EGL的初始化,創(chuàng)建Surface,紋理映射等操作;
/**
 * 窗口輸出表面
 */
public class WindowInputSurface {
    private static final int EGL_RECORDABLE_ANDROID = 0x3142;

    //gl設(shè)備
    private EGLDisplay mEGLDisplay            = EGL14.EGL_NO_DISPLAY;
    //gl環(huán)境
    private EGLContext mEGLContext            = EGL14.EGL_NO_CONTEXT;
    //gl表面
    private EGLSurface mEGLSurface            = EGL14.EGL_NO_SURFACE;

    //gl屬性
    private static final int[] mAttribList    = {
            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_RECORDABLE_ANDROID, 1,
            EGL14.EGL_NONE
    };

    //配置context屬性
    private static final int[] mContextAttlist = {
            EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
            EGL14.EGL_NONE
    };

    //surface屬性
    private static final int[] mSurfaceAttribs = {
            EGL14.EGL_NONE
    };

    private Surface    mSurface;

    public WindowInputSurface(Surface mSurface) {
        this.mSurface = mSurface;
        init();
    }

    //初始化配置
    private final void init(){
        //創(chuàng)建設(shè)備
        mEGLDisplay         = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("unable to get EGL14 display");
        }

        //初始化設(shè)備
        int[] version       = new int[2];
        if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
            throw new RuntimeException("unable to initialize EGL14");
        }

        //根據(jù)屬性選擇最優(yōu)的配置
        EGLConfig[] configs = new EGLConfig[1];
        int[] numConfigs    = new int[1];
        EGL14.eglChooseConfig(mEGLDisplay, mAttribList, 0, configs, 0, configs.length,
                numConfigs, 0);

        checkEglError("eglCreateContext RGB888+recordable ES2");

        mEGLContext         = EGL14.eglCreateContext(
                mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
                mContextAttlist, 0);

        checkEglError("eglCreateContext");

        mEGLSurface         = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
                mSurfaceAttribs, 0);
        checkEglError("eglCreateWindowSurface");

    }

    /**
     * 準(zhǔn)備當(dāng)前渲染環(huán)境
     */
    public final void makeCurrent() {
        EGL14.eglMakeCurrent(mEGLDisplay,
                mEGLSurface, mEGLSurface, mEGLContext);
        checkEglError("eglMakeCurrent");
    }

    /**
     * 生產(chǎn)數(shù)據(jù)
     * @return
     */
    public final boolean swapBuffers() {
        boolean result = EGL14.eglSwapBuffers(mEGLDisplay, mEGLSurface);
        checkEglError("eglSwapBuffers");
        return result;
    }

    /**
     * 設(shè)置下一幀數(shù)據(jù)時(shí)間戳
     * @param nsecs
     */
    public final void setPresentationTime(long nsecs) {
        EGLExt.eglPresentationTimeANDROID(mEGLDisplay, mEGLSurface, nsecs);
        checkEglError("eglPresentationTimeANDROID");
    }

    /**
     * 釋放資源
     */
    public final void release() {
        if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
            EGL14.eglMakeCurrent(mEGLDisplay,
                    EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
                    EGL14.EGL_NO_CONTEXT);
            EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
            EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
            EGL14.eglReleaseThread();
            EGL14.eglTerminate(mEGLDisplay);
        }
        mSurface.release();
        mEGLDisplay = EGL14.EGL_NO_DISPLAY;
        mEGLContext = EGL14.EGL_NO_CONTEXT;
        mEGLSurface = EGL14.EGL_NO_SURFACE;
        mSurface = null;
    }

    //檢查錯(cuò)誤
    private final void checkEglError(String msg) {
        int error;
        if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
            throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
        }
    }
}
4、EncodeManager采集數(shù)據(jù)、渲染生成Mp4核心類;
/**
 * 編解碼管理器
 */
public class EncodeManager implements Runnable{
    private static final String TAG             = "EncodeManager";

    //編碼寬度
    private static final int ENCODER_WIDTH      = 640;
    //編碼高度
    private static final int ENCODER_HEIGHT     = 480;
    //bit rate
    private static final int ENCODER_BIT_RATE   = 6000000;
    //視頻格式
    private static final String MIME_TYPE       = "video/avc";
    //幀率
    private static final int FRAME_RATE         = 30;
    //I幀間隔時(shí)間
    private static final int IFRAME_INTERVAL    = 5;
    //Mp4輸出目錄
    private static final File OUTPUT_DIR        = Environment.getExternalStorageDirectory();

    private MediaCodec.BufferInfo mBufferInfo;
    private MediaCodec            mEncoder;
    private MediaMuxer            mMediaMuxer;
    private WindowInputSurface    mWindowInputSurface;
    private SurfaceTextureWrapper mSurfaceTexture;
    private Camera                mCamera;

    private Thread                mStartPreviewThread;

    private boolean               mStartPreview;

    //視頻軌道
    private int                   mTrackIndex;
    //判斷muxer是否啟動
    private boolean               mMuxerStarted;

    private MediaCallback         mMediaCallback;

    private String                mOutputPath;
    public EncodeManager(MediaCallback mediaCallback) {
        mMediaCallback = mediaCallback;
    }

    /**
     * 開始預(yù)覽數(shù)據(jù),啟動入口
     */
    public final void start(){
        mStartPreview       = true;
        mStartPreviewThread = new Thread(this);
        mStartPreviewThread.start();
    }

    /**
     * 停止錄制
     */
    public final void stop(){
        mStartPreview       = false;
        Log.v(TAG,"開始停止錄像");
    }


    @Override
    public void run() {
        try{
            //打開照相機(jī)
            openCamera();
            //初始化編碼器
            initEncoder();
            //準(zhǔn)備渲染環(huán)境
            mWindowInputSurface.makeCurrent();
            //紋理映射
            attchSurfaceTexture();
            //啟動照相預(yù)覽
            mCamera.startPreview();
            int frameCount = 0;
            mMediaCallback.onStart();
            while (mStartPreview){
                drainEncoder(false);
                if ((frameCount % 15) == 0) {
                    String fragmentShader = null;
                    if ((frameCount & 0x01) != 0) {
                        fragmentShader = SurfaceTextureRender.SWAPPED_FRAGMENT_SHADER;
                    }
                    mSurfaceTexture.changeFragmentShader(fragmentShader);
                }
                frameCount++;
                //等待新數(shù)據(jù)
                mSurfaceTexture.awaitNewImage();
                //寫入一幀數(shù)據(jù)
                mSurfaceTexture.drawImage();
                //設(shè)置時(shí)間戳
                mWindowInputSurface.setPresentationTime(
                        mSurfaceTexture.getSurfaceTexture().getTimestamp());
                mWindowInputSurface.swapBuffers();
                Log.v(TAG,"window input surface swap buffer success ");
            }

            //停止預(yù)覽
            if(!mStartPreview){
                Log.v(TAG,"stop preview start >>>> ");
                drainEncoder(true);
                Log.v(TAG,"停止錄像成功");
            }
        }catch (Exception e){
            FileLoger.LOGE(TAG,"啟動發(fā)生異常--->>" + e.getMessage());
        }finally {
            release();
            //完成一次錄像
            mMediaCallback.onComplete();
        }
    }

    //編碼
    private final void drainEncoder(boolean endOfStream){
        final int TIMEOUT_USEC = 10000;
        //結(jié)束編碼
        if (endOfStream) {
            mEncoder.signalEndOfInputStream();
        }

        ByteBuffer[] outputBuffers = mEncoder.getOutputBuffers();

        boolean outPutEnd          = true;

        while(outPutEnd){
            //緩沖索引
            int bufferIndex        = mEncoder.dequeueOutputBuffer(
                    mBufferInfo, TIMEOUT_USEC);
            switch (bufferIndex){
                //等待數(shù)據(jù)狀態(tài)
                case MediaCodec.INFO_TRY_AGAIN_LATER:
                    //如果不是結(jié)束流標(biāo)志,剛跳出循環(huán)
                    if (!endOfStream) {
                        outPutEnd  = false;
                    }
                    break;
                    //
                case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                    outputBuffers  = mEncoder.getOutputBuffers();
                    break;
                //編碼格式改變
                case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                    if (mMuxerStarted) {
                        throw new RuntimeException("format changed twice");
                    }
                    MediaFormat newFormat = mEncoder.getOutputFormat();
                    // now that we have the Magic Goodies, start the muxer
                    mTrackIndex           = mMediaMuxer.addTrack(newFormat);
                    mMediaMuxer.start();
                    mMuxerStarted         = true;
                    break;
                default:
                    if(bufferIndex >=0){
                        ByteBuffer buffer = outputBuffers[bufferIndex];
                        if (buffer == null) {
                            throw new RuntimeException("encoderOutputBuffer " + bufferIndex +
                                    " was null");
                        }

                        if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                            mBufferInfo.size = 0;
                        }

                        if (mBufferInfo.size != 0) {
                            if (!mMuxerStarted) {
                                throw new RuntimeException("muxer hasn't started");
                            }
                            // adjust the ByteBuffer values to match BufferInfo (not needed?)
                            buffer.position(mBufferInfo.offset);
                            buffer.limit(mBufferInfo.offset + mBufferInfo.size);
                            //寫入一幀數(shù)據(jù)
                            mMediaMuxer.writeSampleData(mTrackIndex, buffer, mBufferInfo);
                        }

                        //釋放buffer
                        mEncoder.releaseOutputBuffer(bufferIndex, false);
                    }

                    if ((mBufferInfo.flags &
                            MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                        outPutEnd = false;
                    }
                    break;
            }
        }
    }

    //打開攝像頭
    private final void openCamera(){
        if (mCamera != null) {
            throw new RuntimeException("camera already initialized");
        }
        Camera.CameraInfo info = new Camera.CameraInfo();
        // Try to find a front-facing camera (e.g. for videoconferencing).
        int numCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numCameras; i++) {
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                mCamera = Camera.open(i);
                break;
            }
        }
        if (mCamera == null) {
            mCamera = Camera.open();    // opens first back-facing camera
        }

        if (mCamera == null) {
            throw new RuntimeException("Unable to open camera");
        }

        Camera.Parameters parms = mCamera.getParameters();
        choosePreviewSize(parms);
        // leave the frame rate set to default
        mCamera.setParameters(parms);

    }

    //選擇預(yù)覽大小
    private final void choosePreviewSize(Camera.Parameters parms) {
        Camera.Size ppsfv = parms.getPreferredPreviewSizeForVideo();
        for (Camera.Size size : parms.getSupportedPreviewSizes()) {
            if (size.width == ENCODER_WIDTH
                    && size.height == ENCODER_HEIGHT) {
                parms.setPreviewSize(ENCODER_WIDTH, ENCODER_HEIGHT);
                return;
            }
        }

        if (ppsfv != null) {
            parms.setPreviewSize(ppsfv.width, ppsfv.height);
        }
    }

    //初始化編碼器
    private final void initEncoder(){
        mBufferInfo        = new MediaCodec.BufferInfo();
        MediaFormat format = MediaFormat.createVideoFormat(
                MIME_TYPE,ENCODER_WIDTH, ENCODER_HEIGHT);
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_BIT_RATE, ENCODER_BIT_RATE);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
        mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
        mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mWindowInputSurface = new WindowInputSurface(
                mEncoder.createInputSurface());
        mEncoder.start();
        //輸出目錄
        mOutputPath         = new File(OUTPUT_DIR,
                 ENCODER_WIDTH + "x" + ENCODER_HEIGHT + ".mp4").toString();
        try {
            mMediaMuxer     = new MediaMuxer(mOutputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        } catch (IOException ioe) {
            throw new RuntimeException("MediaMuxer creation failed", ioe);
        }
        mTrackIndex = -1;
        mMuxerStarted = false;
    }

    public final String getOutPath(){
        return mOutputPath;
    }

    //紋理映射
    private final void attchSurfaceTexture() throws IOException {
        mSurfaceTexture   = new SurfaceTextureWrapper();
        SurfaceTexture st = mSurfaceTexture.getSurfaceTexture();
        mCamera.setPreviewTexture(st);
    }

    //釋放資源
    private final void release(){
        //釋放camera
        if(null != mCamera){
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }

        //釋放編碼器
        if (mEncoder != null) {
            mEncoder.stop();
            mEncoder.release();
            mEncoder = null;
        }

        //釋放緩沖區(qū)
        if (mWindowInputSurface != null) {
            mWindowInputSurface.release();
            mWindowInputSurface = null;
        }

        //釋放MediaMuxer資源
        if (mMediaMuxer != null) {
            mMediaMuxer.stop();
            mMediaMuxer.release();
            mMediaMuxer = null;
        }

        //釋放紋理
        if (mSurfaceTexture != null) {
            mSurfaceTexture.release();
            mSurfaceTexture = null;
        }
    }
}

以上就是全部核心的代碼,稍后有時(shí)間了會再詳細(xì)的介紹下里面所需要用到的知識,流媒體,opengl等,敬請期待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,157評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內(nèi)容