Gravação de arquivos de vídeo no Android

Antes de falarmos sobre gravação e reprodução de áudio, agora falamos sobre gravação de vídeo. Na verdade, a gravação de vídeo ainda usa o MediaRecorder e coopera com a câmera para operar. Então, aqui explicamos em detalhes a prática da gravação de vídeo. Quanto à reprodução de vídeo, devido a uma série de problemas relacionados à troca de tela cheia, gerenciamento de ciclo de vida, etc., não vou escrever um blog aqui. Os alunos interessados ​​podem encontrar minha estrutura em meus recursos, que contém casos de reprodução de vídeo.

Bem, começamos a gravar o vídeo de aprendizagem.

1. Métodos comuns de media player MediaRecorder (comum para gravação e gravação)

  • redefinir: Redefina o recurso de gravação.
  • preparar: preparar para gravar.
  • start: inicia a gravação.
  • parar: termina a gravação.
  • lançamento: libera recursos de gravação.
  • setOnErrorListener: Defina o ouvinte de erro. Pode monitorar exceções do servidor e eventos de erro desconhecidos. É necessário implementar o método onError da interface MediaRecorder.OnErrorListener.
  • setOnInfoListener: Defina o ouvinte de informações. Você pode monitorar o evento de término da gravação, incluindo atingir a duração da gravação ou atingir o tamanho da gravação. É necessário implementar o método onInfo da interface MediaRecorder.OnInfoListener.
  • setMaxDuration: Defina a duração máxima da gravação, em milissegundos.
  • setMaxFileSize: Defina o tamanho máximo do arquivo gravável, em bytes.
  • setOutputFile: Defina o caminho do arquivo de saída.

2. Métodos comuns usados ​​por MeidaRecorder para gravação

  • setCamera: Defina o objeto da câmera.
  • setPreviewDisplay: Defina a interface de visualização. O objeto de interface de visualização pode ser obtido por meio do método getSurface do objeto SurfaceHolder.
  • setOrientationHint: Defina o ângulo da visualização. Defina como 90 para tirar fotos, o que significa que a interface é girada 90 graus da horizontal para a vertical.
  • setVideoSource: Defina a fonte de vídeo. Geralmente, use VideoSource.CAMERA para representar a câmera.
  • setOutputFormat: Defina o formato de saída da mídia. O valor do formato de saída da mídia é mostrado na Tabela 1.
tabela 1
O formato de saída da classe OutputFormat Classificação de formato nome da extensão Descrição do formato
AMR_NB Áudio .amr Formato de banda estreita
AMR_WB Áudio .amr Formato de banda larga
AAC_ADTS Áudio .aac Formato de stream de transporte de áudio avançado
MPEG_4 vídeo .mp4 Formato MPEG4
THREE_GPP vídeo .3gp Formato 3GP
  • setVideoEncoder: Defina o codificador de vídeo. Geralmente, VideoEncoder.MPEG_4_SP é usado para representar a codificação MPEG4. Este método é chamado após o método setOutputFormat, caso contrário, um erro java.lang.IllegalStateException será relatado.
  • setVideoSize: Defina a resolução do vídeo.
  • setVideoFrameRate: Defina o número de quadros de vídeo gravados por segundo. Quanto maior for o vídeo, mais coerente será o vídeo; é claro, quanto maior será o arquivo de vídeo final.
  • setVideoEncodingBitRate: Defina o número de bytes de vídeo gravados por segundo. Quanto maior o vídeo, mais nítido é o vídeo, basta definir um de setVideoFrameRate e setVideoEncodingBitRate.

3. Exemplo de código

Pedido de Acesso

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

Código de layout activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/fr_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <Button
        android:id="@+id/bt_record"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="录制视频"/>
</RelativeLayout>

MainActivity.java

public class MainActivity extends WaterPermissionActivity implements MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener {

    private TextView bt_record;
    private FrameLayout fr_root;
    private SurfaceHolder mHolder; // 声明一个表面持有者对象
    private Camera mCamera; // 声明一个相机对象
    private MediaRecorder mMediaRecorder;
    private String moviePath;//视频路径

    @Override
    protected MvcBaseModel getModelImp() {
        return null;
    }

    @Override
    protected int getContentLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initWidget() {
        fr_root = findViewById(R.id.fr_root);
        bt_record = findViewById(R.id.bt_record);
        bt_record.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    //按下,开始录制
                    getPath();//初始化视频路径
                    initRecord();//开始录制
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    //松开,停止录制
                    cancelRecord(); // 取消录制操作
                    freeCamera(); // 释放相机资源
                }
                return true;
            }
        });
        requestPermission(WRITE_EXTERNAL_STORAGE);
    }

    @Override
    protected void doSDWrite() {
        requestPermission(READ_EXTERNAL_STORAGE);
    }

    @Override
    protected void doSDRead() {
        requestPermission(CAMERA);
    }

    @Override
    protected void doCamera() {
        requestPermission(RECORD);
    }

    @Override
    protected void doRecord() {
        //这里为了请求好动态权限再让Surface初始化才这么弄得,可以在进入录制页面就请求好动态权限,就不用这么写了。
        SurfaceView surfaceView = new SurfaceView(this);
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        surfaceView.setLayoutParams(params);
        fr_root.addView(surfaceView);
        // 获取表面视图的表面持有者
        mHolder = surfaceView.getHolder();
        // 给表面持有者添加表面变更监听器
        mHolder.addCallback(mSurfaceCallback);
    }

    /**
     * 初始化录制操作,开始录制调用这个方法
     */
    private void initRecord() {
        mMediaRecorder = new MediaRecorder(); // 创建一个媒体录制器
        mMediaRecorder.setCamera(mCamera); // 设置媒体录制器的摄像头
        mMediaRecorder.setOnErrorListener(this); // 设置媒体录制器的错误监听器
        mMediaRecorder.setOnInfoListener(this); // 设置媒体录制器的信息监听器
        mMediaRecorder.setPreviewDisplay(mHolder.getSurface()); // 设置媒体录制器的预览界面
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // 设置视频源为摄像头
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置媒体的输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置媒体的音频编码器
        // 如果录像报错:MediaRecorder start failed: -19
        // 试试把setVideoSize和setVideoFrameRate注释掉,因为尺寸设置必须为摄像头所支持,否则报错
         mMediaRecorder.setVideoSize(2280, 1080); // 设置视频的分辨率
        // mMediaRecorder.setVideoFrameRate(16); // 设置视频每秒录制的帧数
        // setVideoFrameRate与setVideoEncodingBitRate设置其一即可
        mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512); // 设置视频每秒录制的字节数
        mMediaRecorder.setOrientationHint(90); // 输出旋转90度,也就是保持竖屏录制
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); // 设置媒体的视频编码器
        mMediaRecorder.setMaxDuration(10 * 1000); // 设置媒体的最大录制时长
        // mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小
        // setMaxFileSize与setMaxDuration设置其一即可
        mMediaRecorder.setOutputFile(moviePath); // 设置媒体文件的保存路径
        try {
            mMediaRecorder.prepare(); // 媒体录制器准备就绪
            mMediaRecorder.start(); // 媒体录制器开始录制
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 定义一个表面持有者的变更监听器
     */
    private SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
        // 在表面视图创建时触发
        public void surfaceCreated(SurfaceHolder holder) {
            initCamera(); // 初始化相机
        }

        // 在表面视图变更时触发
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }

        // 在表面视图销毁时触发
        public void surfaceDestroyed(SurfaceHolder holder) {
            freeCamera(); // 释放相机资源
        }
    };

    /**
     * 释放相机资源
     */
    private void freeCamera() {
        if (mCamera != null) {
            mCamera.stopPreview(); // 停止预览
            mCamera.lock(); // 锁定相机,即关闭相机
            mCamera.release(); // 释放相机资源
            mCamera = null;
        }
    }

    /**
     * 初始化相机操作
     */
    private void initCamera() {
        if (mCamera != null) {
            freeCamera();
        }
        try {
            // 打开摄像头,默认后置摄像头
            mCamera = Camera.open();
            // 设置相机的展示角度
            mCamera.setDisplayOrientation(90);
            // 设置相机的预览界面
            mCamera.setPreviewDisplay(mHolder);
            // 开始预览画面
            mCamera.startPreview();
            // 解锁相机,即打开相机
            mCamera.unlock();
        } catch (Exception e) {
            e.printStackTrace();
            freeCamera();
        }
    }

    /**
     * 录制前创建一个空文件并获取路径
     */
    private void getPath() {
        List<String> list = new ArrayList<>();
        list.add("record");
        String dirPath = PathGetUtil.getLongwayPath(this, list);
        File fileDir = new File(dirPath);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        File fileVoice = new File(dirPath, "movie" + System.currentTimeMillis() + ".mp4");
        if (!fileVoice.exists()) {
            try {
                fileVoice.createNewFile();
                moviePath = fileVoice.getPath();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onError(MediaRecorder mr, int what, int extra) {
        if (mr != null) {
            mr.reset();  // 重置媒体录制器
        }
    }

    @Override
    public void onInfo(MediaRecorder mr, int what, int extra) {
        // 录制达到最大时长,或者达到文件大小限制,都停止录制
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
                || what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
            cancelRecord();
        }
    }

    /**
     * 取消录制操作,停止录制的时候调用该方法
     */
    private void cancelRecord() {
        if (mMediaRecorder != null) {
            mMediaRecorder.setOnErrorListener(null); // 错误监听器置空
            mMediaRecorder.setPreviewDisplay(null); // 预览界面置空
            try {
                mMediaRecorder.stop(); // 媒体录制器停止录制
            } catch (Exception e) {
                e.printStackTrace();
            }
            mMediaRecorder.release(); // 媒体录制器释放资源
            mMediaRecorder = null;
        }
    }
}

Da mesma forma, como o vídeo que gravamos não foi processado, a qualidade do vídeo não era muito alta e era muito confusa. Portanto, se você quiser ser mais claro, pode considerar alterar a parte da configuração da câmera para Camera2 ou um CameraX melhor. Não vou realizar operações específicas aqui, os princípios são os mesmos, você pode tentar se tiver interesse.

Acho que você gosta

Origin blog.csdn.net/weixin_38322371/article/details/115249593
Recomendado
Clasificación