使用OpenGL实现视频录制

之前,我写过一篇文章,用Camera2和GLSurface实现预览:https://blog.csdn.net/qq_36391075/article/details/81631461
今天,来实现录制视频:

思路:
  1. 通过MediaCodec创建一个用于输入的Surface
  2. .通过通过camera预览时的上下文EGL创建OpenGL的环境,根据上面得到的Surface创建EGLSuface。
  3. 通过camera预览时的绑定的纹理id,进行纹理绘制。
  4. 交换数据,让数据输入金Surface。使用AudioReocod进行声音的采集
  5. 通过Mediacodec和MediaMuxer进行数据的封装
MediaCodec

mediacodec可以用来获得安卓底层的多媒体编码,可以用来编码和解码,它是安卓low-level多媒体基础框架的重要组成部分。它经常和 MediaExtractor, MediaSync, MediaMuxer, MediaCrypto, MediaDrm, Image, Surface, AudioTrack一起使用。
这里写图片描述

通过上图可以看出,mediacodec的作用是处理输入的数据生成输出数据。首先生成一个输入数据缓冲区,将数据填入缓冲区提供给codec,codec会采用异步的方式处理这些输入的数据,然后将填满输出缓冲区提供给消费者,消费者消费完后将缓冲区返还给codec。
关于它的详细介绍可以参考:https://juejin.im/entry/5aa234f751882555712bf210

MediaMuxer

MediaMuxer的作用是生成音频或视频文件;还可以把音频与视频混合成一个音视频文件

相关API介绍:

  • MediaMuxer(String path, int format):path:输出文件的名称 format:输出文件的格式;当前只支持MP4格式;
  • addTrack(MediaFormat format):添加通道;我们更多的是使用MediaCodec.getOutpurForma()或Extractor.getTrackFormat(int index)来获取MediaFormat;也可以自己创建;
  • start():开始合成文件
  • writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo):把ByteBuffer中的数据写入到在构造器设置的文件中;
  • stop():停止合成文件
  • release():释放资源

关于它的使用可以参考:https://www.cnblogs.com/renhui/p/7474096.html

根据思路,首先,我们得先创建一个关于画面的MeidaCodec:

//在VideoRecordEncode类中
 public void prepare(){

        Log.d(TAG, "prepare: "+Thread.currentThread().getName());
        try {
            mEnOS = false;
            mViedeoEncode = MediaCodec.createEncoderByType(MIME_TYPE);
            MediaFormat format =  MediaFormat.createVideoFormat(MIME_TYPE,mWidth,mHeight);
            format.setInteger(MediaFormat.KEY_BIT_RATE,calcBitRate());
            format.setInteger(MediaFormat.KEY_FRAME_RATE,FRAME_RATE);
            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,10);
            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                    MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
            mViedeoEncode.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);

            //得到Surface用于编码
            mSurface = mViedeoEncode.createInputSurface();
            mViedeoEncode.start();
            mPrepareLisnter.onPrepare(this);

        }catch (IOException e){
            e.printStackTrace();
        }

    }

Surface创建好后,我们绑定EGL的上下文:在`onPrepare(this);回调中:

 private  onFramPrepareLisnter lisnter = new onFramPrepareLisnter() {
        @Override
        public void onPrepare(VideoRecordEncode encode) {
           mPresenter.setVideoEncode(encode);
        }
    };

public void setVideoEncode(VideoRecordEncode encode){
        mViewController.setVideoEncode(encode);
    }

 @Override
    public void setVideoEncode(VideoRecordEncode encode) {
        mRecordView.setVideoEndoer(encode);//mRecodView是GLSrurace的子类

    }

public void setVideoEndoer(final VideoRecordEncode endoer){
        //获得OpenGL中的线程
        queueEvent(new Runnable() {
            @Override
            public void run() {
                synchronized (mRender){
                    endoer.setEGLContext(EGL14.eglGetCurrentContext(),mTextId);
                    mRender.mEncode = endoer;
                }
            }
        });
    }

    public void setEGLContext(EGLContext context,int texId){
        mShare_Context = context;
        mTexId = texId;
       mHandler.setEGLContext(mShare_Context,mSurface,mTexId);//创建上下文
    }

与此同时,在VideoRecordEncode类中,在它被创建的同时,让MediaCodec的对象作用在一个线程中,且让用EGL绘制纹理作用于另一个线程中:

 public VideoRecordEncode(VideoMediaMuxer muxer,onFramPrepareLisnter prepareLisnter, int width, int height) {
        mWidth = width;
        mHeight = height;
        mPrepareLisnter = prepareLisnter;
        mMuxer = muxer;
        mBfferInfo = new MediaCodec.BufferInfo();
        mHandler = RenderHandler.createRenderHandler();
        synchronized (mSync){
            new Thread(this).start();
            try {
                mSync.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }

    public static RenderHandler createRenderHandler(){
        RenderHandler handler = new RenderHandler();
        new Thread(handler).start();
        synchronized (handler.mSyn){
            try {
                handler.mSyn.wait();
            }catch (InterruptedException e){
                return null;
            }
        }

        return handler;
    }

RenderHanlder是专门用于EGL绘制的一个类:当它启动的时候,进入run方法,这个时候,EGL的环境还没有创建,因此我们用一个标志符来标志是否创建EGLContext:

@Override
    public void run() {

        synchronized (mSyn){
            mRequestRelease= mRequestEGLContext = false;
            mRequestDraw = 0;
            mSyn.notifyAll();
        }
        boolean localRequestDraw = false;
        for(;;){
            synchronized (mSyn){
                if(mRequestRelease) break;
                
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值