Android 音视频系列-视频录制

  • Post author:
  • Post category:其他

1、录制前的预览

一般而言,我们在录制之前,都需要预览。

我们来回归下利用Camera2进行预览的步骤:

  • 通过CameraManager获取可用的相机设备列表。
  • 通过CameraManager拿到对应相机的参数
  • 调用openCamera打开相机。
  • 在回调中创建CaptureRequestBuilder与CameraCaptureSession。其中,要将我们的Surface添加到CaptureRequestBuilder中,这里我们还是使用TextureView,通过其SurfaceTexture来创建Surface。
  • 调用CameraCaptureSession的setRepeatingRequest来开启预览。

总得来说,预览是比较简单的,拍照与录制时的预览没区别

而安卓上录制有2种方式:

  • 利用系统api MediaRecorder ;
  • 使用MediaCodeC 自定义录制;

第一种方式比第二种简单很多,下面先来学习第一种方式。

2、利用MediaRecorder 进行录制

MediaRecorder是Android Frameworl提供给开发者的一套用于音频或视频录制的API。我们可以通过它来录制音频或者视频。当然录制视频的时候就需要Camera来配合了,下面我们来看下怎么来配置一个可以录制视频的MediaRecorder。

音频与视频的来源

setAudioSource(int audio_source)

在MediaRecorder里面有一个内部类AudioSource,里面定义了一些静态常量来表示各个音频的来源,我们这里用AudioSource.MIC(麦克风)

setVideoSource(int video_source)

同样的在MediaRecorder中有一个VideoSource的内部类,它只有三个静态常量,DEFAULT、CAMERA、SURFACE。CAMERA是与Camera搭配使用的,它需要给MediaRecord通过setCamera(Camera camera)传一个Camera过来,这里我们用Camera2,所以需要用SURFACE作为视频源,还记得我们上一篇总结的,Camera是通过CaptureRequest和CameraCaptureSession来将图像数据发送到一些我们设置的目标Surface中,所以这里我们用VideoSource.SURFACE。后面我们就可以通过MediaRecorder的getSurface()方法来拿到它的Surface。

这两个方法都需要在setOutputFormat之前调用,如果在之后调用就会抛IllegalStateException异常。

输出格式

setOutputFormat(int output_format)

设置录制过程中输出文件的格式,它需要在setAudioSource()/setVideoSource()之后调用,在prepare()之前调用,同时需要在设置录制参数和解码器之前调用。同样MediaRecorder中的内部类OutputFormat定义了一些静态常量来表示媒体格式。当用H.263视频解码器和AMR音频解码器时,推荐使用3GP格式,对用OutputFormat.THREE_GPP。

输出目录

setOutputFile(String path)

在setOutputFormat()之后,prepare()之前调用

视频的尺寸

setVideoSize(int width, int height)

设置录制视频的宽高

视频码率

setVideoEncodingBitRate(int bitRate)

视频帧率

setVideoFrameRate(int rate)

注意:在某些自动帧率的设备上,这个设置将作为最大帧率而不是一个固定的帧率,实际的帧率会随着光照条件变化而变化。

音频编码器

setAudioEncoder(int audio_encoder)

设置录制的音频编码器,如果没有设置,则输出文件中将不会包含音轨,在setOutputFormat之后prepare之前调用此方法。下面是所有的音频编码器的值

3、录制

介绍完所需要的设置参数后,下面我们来实战一下,测试代码如下:

class VideoRecorder {

    private static final String OUTPUT_FILE_PATH = "/sdcard/test_jq.mp4";

    private TextureView mTextureView;
    private MediaRecorder mMediaRecorder;
    private Camera mCamera;

    public VideoRecorder(TextureView textureView) {
        mTextureView = textureView;
        mMediaRecorder = new MediaRecorder();
        initCamera();
    }

    public void start() {
        configRecorder();

        try {
            mMediaRecorder.prepare();
            mMediaRecorder.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void pause() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder.pause();
        }
    }

    public void resume() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            mMediaRecorder.resume();
        }
    }

    public void stop() {
        mMediaRecorder.stop();
    }

    public void destroy() {
        mMediaRecorder.stop();
        mMediaRecorder.release();
        mCamera.lock();
    }

    private void configRecorder() {
        mMediaRecorder.setCamera(mCamera);
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setVideoFrameRate(30); //录制的视频帧率
        mMediaRecorder.setVideoSize(1280,1920); //录制视频分辨率
        mMediaRecorder.setVideoEncodingBitRate(8*1920*1080);

        mMediaRecorder.setPreviewDisplay(new Surface(mTextureView.getSurfaceTexture()));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            mMediaRecorder.setOutputFile(new File(OUTPUT_FILE_PATH));
        }
    }

    private void initCamera() {
        mCamera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
        mCamera.setParameters(parameters);

        //设置旋转角度,顺时针方向,因为默认是逆向90度的,这样图像就是正常显示了
        mCamera.setDisplayOrientation(90);
        mCamera.unlock();
    }

总结

其实视频录制的过程还是比较清晰的,首先,预览跟拍照没什么区别,就是录制的时候构建一个MediaRecorder,然后重新创建CaptureRequest与CameraCaptureSession,然后将MediaRecorder的Surface传进去,这样当CameraCaptureSession创建好之后图像数据就会渲染后MediaRecorder的Surface中去,然后调用MediaRecorder的start()方法开始录制。最后停止录制的时候调用MediaRecorder的stop()方法停止录制,并重新创建预览的CaptureRequest和CameraCaptureSession重新开启预览。更多细节可以参考https://github.com/googlesamples/android-Camera2Video




版权声明:本文为linfeng24原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。