使用Camera+MediaCodec录制编译音视频

作者: Zhaoss | 来源:发表于2019-06-24 18:35 被阅读7次

之前写过一个开源项目仿微信视频拍摄UI基于 ffmpeg的视频录制编辑

使用的是VCamera库来录制,效果很差, 主要是因为ffmpeg的so库编译版本不支持targetSdkVersion26以上, 导致现在大部分项目都用不了, 而且优化不是很好

所以我用了基于Camera录制源+MediaCodec编码 替换了原本的录制方法, 并更新了ffmpeg库LanSoEditor 速度更快 兼容更好

1. 首先是打开camera预览画面

    //这些没什么好说的 正常初始化Camera对象, 注意getCameraDisplayOrientation()这个方法, 
    //因为Camera得到的画面是歪的, 所以我们需要旋转一下
    public void openCamera(Activity activity, int cameraId, SurfaceHolder surfaceHolder){
        ...
        mCamera = Camera.open(cameraId);
        displayOrientation = getCameraDisplayOrientation(activity, cameraId);
        mCamera.setDisplayOrientation(displayOrientation);
        mCamera.setPreviewDisplay(surfaceHolder);
        mCamera.setPreviewCallback(previewCallback);

        Camera.Parameters parameters = mCamera.getParameters();
        previewSize = getPreviewSize();
        parameters.setPreviewSize(previewSize[0], previewSize[1]);
        parameters.setFocusMode(getAutoFocus());
        parameters.setPictureFormat(ImageFormat.JPEG);
        parameters.setPreviewFormat(ImageFormat.NV21);

        mCamera.setParameters(parameters);
        mCamera.startPreview();
        ...
    }
    
    //在回调中 我们通过mYUVQueue容器 把数据传递给解码器, 进行每帧解码
     mCameraHelp.setPreviewCallback(new Camera.PreviewCallback() {
        @Override
        public void onPreviewFrame(byte[] data, Camera camera) {
             ...
             mYUVQueue.add(data);
             ...
        }
    });

2. 当开始录制的时候 初始化AvcEncoder(重点)

//首先是构造函数里面, 我们初始化MediaCodec编码器(视频宽高,码率,yuv420色彩,帧数,编码格式avc)
public AvcEncoder(int width, int height, ArrayBlockingQueue<byte[]> YUVQueue) {
        ...
        MediaFormat mediaFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);
        mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar);
        mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, width*height*5);
        mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
        mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
        mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
        mediaCodec.configure(mediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        mediaCodec.start();
        ...
    }
    
    //开始编码, 编译出来的数据是h264
    public void startEncoder(final String videoPath, final boolean isFrontCamera){
        //循环从YUVQueue里面拿取每帧数据
        while (isRunning.get()) {
            //把nv21转nv12
            NV21ToNV12(input,yuv420sp, width, height);
            //如果是前置摄像头的话 要上下颠倒一下画面
            rotateYUV420Degree180(yuv420sp, width, height);
            //此步就是mediaCodec转码 有兴趣去看详细代码
            ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
            ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
        }
    }

3. 最后一步就是使用ffmpeg把h264转mp4

    //ffmpeg -i in -vcodec copy -f mp4 put
    //因为我的功能里有分段录制 所以我是先把h264转ts 然后多个ts合成mp4

4. 视频录制就完成了, 接下来是音频录制编码 初始化AudioRecordUtil

    public AudioRecordUtil(final String path){
        bufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
        //麦克风 采样率 单声道 音频格式, 缓存大小
        audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRateInHz, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
        mediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
        ...
    }
    
    //和视频编码一样 也是循环
    public void startRecord(){
        while (isRecording.get()) {
            //拿到音频数据
            audioRecord.read(data, 0, bufferSize);
            //pcm转aac 有兴趣去看详细代码
            encodeData(data);
        }
    }

5. 最后录制完成 使用ffppeg把视频和音频文件合成在一起

    //ffmpeg -i video -1 audio -t vDuration -map 0:v -map 1:a -vcodec copy -acodec copy -absf aac_adtstoasc -y out

6. 还有拍照的逻辑 其实很简单 就是把一帧NV21数据转成jpeg

    YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21, mCameraHelp.getWidth(), mCameraHelp.getHeight(), null);
    yuvimage.compressToJpeg(new Rect(0, 0, yuvimage.getWidth(), yuvimage.getHeight()), 100, fos);

到这里 其实主要逻辑就结束了 其余的视频编辑操作(裁剪, 截取, 添加水印, 播放速度等等)这些其实就是简单的ffmpeg语句, 大家自行看代码也能理解, 之前我也有文章讲过这些

仿微信视频拍摄UI, 基于ffmpeg的视频录制编辑(上)
仿微信视频拍摄UI, 基于ffmpeg的视频录制编辑(下)

希望能对你有所帮助

相关文章

网友评论

    本文标题:使用Camera+MediaCodec录制编译音视频

    本文链接:https://www.haomeiwen.com/subject/fkxaqctx.html