MediaCodec硬编码成H264视频流

android提供了一个强大的编解码类MediaCodec,可以实现对视频数据的编解码,下面讲一下如何对原始视频数据硬编码成h264格式的流


MediaCodec提供两种方式的输入,一种是将数据写入它的输入缓冲队列里,一种是让MediaCodec建立一个输入Surface,MediaCodec会自动从这个输入Surface中读取数据,因为我做的是录制屏幕的需求,所以我是使用一个Surface输入数据给MediaCodec。MediaCodec编码出来的头两帧是特殊的,分别是sps 和 pps这两帧在解码时要用来配置解码器用的。下面贴出编码器代码,这里我做的是视频实时编码传输,所以编码后的数据我使用socket发送的,大家主要还是看看编码部分的代码就好了:

package com.seewo.seewoair.coder;

/**
 * @author zhangsutao
 * @file VideoCodec.java
 * @brief 视频编解码器基类
 * @date 2016/8/7
 */
public interface VideoCodec {

    String MIME_TYPE = "video/avc";
    int VIDEO_FRAME_PER_SECOND = 15;
    int VIDEO_I_FRAME_INTERVAL = 5;
    int VIDEO_BITRATE = 500 * 8 * 1000;
}



/**
 * @author zhangsutao
 * @file VideoEncoder.java
 * @brief 视频编码器
 * @date 2016/7/29
 */
public class VideoEncoder implements VideoCodec {

    private Worker mWorker;
    private MediaProjection mMediaProjection;
    private VirtualDisplay mVirtualDisplay;
    private Client mClient;
    //写入本地的流,在调试的时候使用
    private DataOutputStream mOutput;
    private final boolean isDebug=true;
    private final String TAG="VideoEncoder";
    private byte[] mFrameByte;

    public VideoEncoder(MediaProjection mediaProjection,Client client) {
        mClient=client;
        mMediaProjection=mediaProjection;
        if(isDebug){
            try {
                mOutput=new  DataOutputStream(new FileOutputStream(new File("/sdcard/h264encode")));;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    protected void onSurfaceCreated(Surface surface, int mWidth, int mHeight) {
        //将屏幕数据与surface进行关联
         mVirtualDisplay = mMediaProjection.createVirtualDisplay("-display",
                mWidth, mHeight, 1, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                surface, null, null);

    }

    protected void onSurfaceDestroyed(Surface surface) {
        mVirtualDisplay.release();
        surface.release();
    }



    public void start() {
        if (mWorker == null) {
            mWorker = new Worker();
            mWorker.setRunning(true);
            mWorker.start();
        }
    }

    public void stop() {
        if (mWorker != null) {
            mWorker.setRunning(false);
            mWorker = null;
        }
        if(mClient!=null){
            if(!mClient.hasRelease()){
                mClient.release();
            }
        }
    }


    private  class Worker extends Thread {
        private MediaCodec.BufferInfo mBufferInfo;
        private MediaCodec mCodec;
        private volatile boolean isRunning;
        private Surface mSurface;
        private final long mTimeoutUsec;
        private int mWidth;
        private int mHeight;

        public Worker() {
            mBufferInfo = new MediaCodec.BufferInfo();
            mTimeoutUsec = 10000l;
        }

        public void setRunning(boolean running) {
            isRunning = running;
        }

        protected void onEncodedSample(MediaCodec.BufferInfo info, ByteBuffer data) {
            if(mFrameByte==null||mFrameByte.length= 0) {
                    ByteBuffer data = mCodec.getOutputBuffer(status);
                    if (data != null) {
                        final int endOfStream = mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM;
                        //传递编码数据
                        if (endOfStream == 0) {
                            onEncodedSample(mBufferInfo, data);
                        }
                        // 一定要记得释放
                        mCodec.releaseOutputBuffer(status, false);
                        if (endOfStream == MediaCodec.BUFFER_FLAG_END_OF_STREAM){
                            return;
                        }
                    }
                }
        }

        private void release() {
            onSurfaceDestroyed(mSurface);
            if(mCodec!=null){
                mCodec.stop();
                mCodec.release();
            }
            if(mOutput!=null){
                try {
                    mOutput.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

        private boolean prepare() {
            // configure video output
            mWidth= SpUtils.readInt(Constans.KEY_DEVICE_WIDTH,-1);
            mHeight=SpUtils.readInt(Constans.KEY_DEVICE_HEIGHT,-1);
            if(mWidth==-1||mHeight==-1){
                return false;
            }
            mClient.connectToServer();
            //发送宽高
            boolean isSuccess1=mClient.sendInt(mWidth);
            boolean isSuccess2=mClient.sendInt(mHeight);
            if(!(isSuccess1&&isSuccess2)){
                isRunning=false;
                mClient.release();
            }
            MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                              MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
            format.setInteger(MediaFormat.KEY_BIT_RATE, VIDEO_BITRATE);
            format.setInteger(MediaFormat.KEY_FRAME_RATE, VIDEO_FRAME_PER_SECOND);
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,VIDEO_I_FRAME_INTERVAL);
            format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER,40);
            try {
                mCodec = MediaCodec.createEncoderByType(MIME_TYPE);
            } catch (IOException e) {
                e.printStackTrace();
                return false;

            }
            mCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            //创建关联的输入surface
            mSurface = mCodec.createInputSurface();
            mCodec.start();
            onSurfaceCreated(mSurface,mWidth,mHeight);
            return true;
        }
    }
}


你可能感兴趣的:(android)