Visão geral do MediaRecorder

O framework multimídia do Android inclui compatibilidade para capturar e codificar vários formatos comuns de vídeo e áudio. Você pode usar as APIs MediaRecorder se elas forem compatíveis com o hardware do dispositivo.

Este documento mostra como usar o MediaRecorder para criar um aplicativo que captura áudio do microfone de um dispositivo, salva o áudio e o toca de volta (com MediaPlayer). Para gravar vídeos, você precisará usar a câmera do dispositivo com . Isso é descrito no guia Câmera.

Observação: o Android Emulator não pode gravar áudio. Teste seu código em um dispositivo real que possa fazer gravações.

Como solicitar permissão para gravar áudio

Para fazer gravações, seu app precisa informar ao usuário que ele acessará a entrada de áudio do dispositivo. Inclua esta tag de permissão no arquivo de manifesto do app:

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    

RECORD_AUDIO é considerada uma permissão "perigosa" porque pode representar um risco para a privacidade do usuário. A partir do Android 6.0 (API de nível 23), um app que usa uma permissão perigosa precisa solicitar a aprovação do usuário no momento da execução. Depois que o usuário conceder a permissão, o app se lembrará dessa ação e não perguntará novamente. O código de amostra abaixo mostra como implementar esse comportamento com ActivityCompat.requestPermissions().

Como criar e executar um MediaRecorder

Inicialize uma nova instância de MediaRecorder com as seguintes chamadas:

  • Defina a origem do áudio com setAudioSource(). Você provavelmente usará MIC.

    Observação: a maioria das origens de áudio (incluindo DEFAULT) aplica um processamento ao sinal de áudio. Para gravar áudio bruto, selecione UNPROCESSED. Alguns dispositivos não são compatíveis com entradas não processadas. Primeiro, chame AudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) para confirmar que ele está disponível. Se não estiver, tente usar VOICE_RECOGNITION, que não emprega AGC nem supressão de ruído. Você pode usar UNPROCESSED como origem de áudio mesmo quando a propriedade não for compatível, mas não há garantia de que o sinal será processado ou não nesse caso.

  • Defina o formato do arquivo de saída com setOutputFormat(). A partir do Android 8.0 (API de nível 26), MediaRecorder é compatível com o formato MPEG2_TS, que é útil para streaming:

    Kotlin

        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
        

    Java

        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
        
  • Defina o nome do arquivo de saída com setOutputFile(). É necessário especificar um descritor de arquivo que represente um arquivo real.
  • Defina o codificador de áudio com setAudioEncoder().
  • Conclua a inicialização chamando prepare().

Inicie e pare o gravador chamando start() e stop(), respectivamente.

Quando você tiver terminado com a instância MediaRecorder, libere os recursos dela o quanto antes, chamando release().

Observação: em dispositivos com o Android 9 (API de nível 28) ou versões posteriores, os apps em execução no segundo plano não podem acessar o microfone. Portanto, seu app gravará áudio somente quando estiver em primeiro plano ou quando você incluir uma instância de MediaRecorder em um serviço de primeiro plano.

Como usar o MediaMuxer para gravar vários canais

A partir do Android 8.0 (API de nível 26), você pode usar um MediaMuxer para gravar vários streams simultâneos de áudio e vídeo. Nas versões anteriores do Android, você só pode gravar uma faixa de áudio e/ou de vídeo por vez.

Use o método addTrack() para misturar várias faixas.

Você também pode adicionar uma ou mais faixas de metadados com informações personalizadas para cada frame, mas apenas em contêineres de MP4. Seu app define o formato e o conteúdo dos metadados.

Como adicionar metadados

Metadados podem ser úteis para processamento off-line. Por exemplo, os dados capturados pelo sensor giroscópio podem ser usados para estabilizar o vídeo.

Quando você adiciona uma faixa de metadados, o formato mime da faixa precisa começar com o prefixo application/. A criação de metadados funciona da mesma forma que a de dados de vídeo ou áudio, mas os dados não vêm de um MediaCodec. Em vez disso, o app passa um ByteBuffer com um carimbo de data/hora associado para o método writeSampleData(). O carimbo de data/hora precisa estar na mesma base de tempo que as faixas de vídeo e áudio.

O arquivo MP4 gerado usa o TextMetaDataSampleEntry definido na seção 12.3.3.2 da especificação ISO BMFF (link em inglês) para sinalizar o formato mime dos metadados. Quando você usa um MediaExtractor para extrair um arquivo que contém faixas de metadados, o formato mime dos metadados aparece como uma instância de MediaFormat.

Amostra de código

A amostra do MediaRecorder (link em inglês) demonstra como fazer uma gravação de vídeo usando o MediaRecorder e a API Camera.

A atividade de exemplo abaixo mostra como usar o MediaRecorder para gravar um arquivo de áudio. Ela também usa o MediaPlayer para tocar o áudio gravado.

Kotlin

    package com.android.audiorecordtest

    import android.Manifest
    import android.content.Context
    import android.content.pm.PackageManager
    import android.media.MediaPlayer
    import android.media.MediaRecorder
    import android.os.Bundle
    import android.support.v4.app.ActivityCompat
    import android.support.v7.app.AppCompatActivity
    import android.util.Log
    import android.view.View.OnClickListener
    import android.view.ViewGroup
    import android.widget.Button
    import android.widget.LinearLayout
    import java.io.IOException

    private const val LOG_TAG = "AudioRecordTest"
    private const val REQUEST_RECORD_AUDIO_PERMISSION = 200

    class AudioRecordTest : AppCompatActivity() {

        private var fileName: String = ""

        private var recordButton: RecordButton? = null
        private var recorder: MediaRecorder? = null

        private var playButton: PlayButton? = null
        private var player: MediaPlayer? = null

        // Requesting permission to RECORD_AUDIO
        private var permissionToRecordAccepted = false
        private var permissions: Array<String> = arrayOf(Manifest.permission.RECORD_AUDIO)

        override fun onRequestPermissionsResult(
                requestCode: Int,
                permissions: Array<String>,
                grantResults: IntArray
        ) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults)
            permissionToRecordAccepted = if (requestCode == REQUEST_RECORD_AUDIO_PERMISSION) {
                grantResults[0] == PackageManager.PERMISSION_GRANTED
            } else {
                false
            }
            if (!permissionToRecordAccepted) finish()
        }

        private fun onRecord(start: Boolean) = if (start) {
            startRecording()
        } else {
            stopRecording()
        }

        private fun onPlay(start: Boolean) = if (start) {
            startPlaying()
        } else {
            stopPlaying()
        }

        private fun startPlaying() {
            player = MediaPlayer().apply {
                try {
                    setDataSource(fileName)
                    prepare()
                    start()
                } catch (e: IOException) {
                    Log.e(LOG_TAG, "prepare() failed")
                }
            }
        }

        private fun stopPlaying() {
            player?.release()
            player = null
        }

        private fun startRecording() {
            recorder = MediaRecorder().apply {
                setAudioSource(MediaRecorder.AudioSource.MIC)
                setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
                setOutputFile(fileName)
                setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)

                try {
                    prepare()
                } catch (e: IOException) {
                    Log.e(LOG_TAG, "prepare() failed")
                }

                start()
            }
        }

        private fun stopRecording() {
            recorder?.apply {
                stop()
                release()
            }
            recorder = null
        }

        internal inner class RecordButton(ctx: Context) : Button(ctx) {

            var mStartRecording = true

            var clicker: OnClickListener = OnClickListener {
                onRecord(mStartRecording)
                text = when (mStartRecording) {
                    true -> "Stop recording"
                    false -> "Start recording"
                }
                mStartRecording = !mStartRecording
            }

            init {
                text = "Start recording"
                setOnClickListener(clicker)
            }
        }

        internal inner class PlayButton(ctx: Context) : Button(ctx) {
            var mStartPlaying = true
            var clicker: OnClickListener = OnClickListener {
                onPlay(mStartPlaying)
                text = when (mStartPlaying) {
                    true -> "Stop playing"
                    false -> "Start playing"
                }
                mStartPlaying = !mStartPlaying
            }

            init {
                text = "Start playing"
                setOnClickListener(clicker)
            }
        }

        override fun onCreate(icicle: Bundle?) {
            super.onCreate(icicle)

            // Record to the external cache directory for visibility
            fileName = "${externalCacheDir.absolutePath}/audiorecordtest.3gp"

            ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION)

            recordButton = RecordButton(this)
            playButton = PlayButton(this)
            val ll = LinearLayout(this).apply {
                addView(recordButton,
                        LinearLayout.LayoutParams(
                                ViewGroup.LayoutParams.WRAP_CONTENT,
                                ViewGroup.LayoutParams.WRAP_CONTENT,
                                0f))
                addView(playButton,
                        LinearLayout.LayoutParams(
                                ViewGroup.LayoutParams.WRAP_CONTENT,
                                ViewGroup.LayoutParams.WRAP_CONTENT,
                                0f))
            }
            setContentView(ll)
        }

        override fun onStop() {
            super.onStop()
            recorder?.release()
            recorder = null
            player?.release()
            player = null
        }
    }
    

Java

    package com.android.audiorecordtest;

    import android.Manifest;
    import android.content.Context;
    import android.content.pm.PackageManager;
    import android.media.MediaPlayer;
    import android.media.MediaRecorder;
    import android.os.Bundle;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.Button;
    import android.widget.LinearLayout;

    import java.io.IOException;

    public class AudioRecordTest extends AppCompatActivity {

        private static final String LOG_TAG = "AudioRecordTest";
        private static final int REQUEST_RECORD_AUDIO_PERMISSION = 200;
        private static String fileName = null;

        private RecordButton recordButton = null;
        private MediaRecorder recorder = null;

        private PlayButton   playButton = null;
        private MediaPlayer   player = null;

        // Requesting permission to RECORD_AUDIO
        private boolean permissionToRecordAccepted = false;
        private String [] permissions = {Manifest.permission.RECORD_AUDIO};

        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
            switch (requestCode){
                case REQUEST_RECORD_AUDIO_PERMISSION:
                    permissionToRecordAccepted  = grantResults[0] == PackageManager.PERMISSION_GRANTED;
                    break;
            }
            if (!permissionToRecordAccepted ) finish();

        }

        private void onRecord(boolean start) {
            if (start) {
                startRecording();
            } else {
                stopRecording();
            }
        }

        private void onPlay(boolean start) {
            if (start) {
                startPlaying();
            } else {
                stopPlaying();
            }
        }

        private void startPlaying() {
            player = new MediaPlayer();
            try {
                player.setDataSource(fileName);
                player.prepare();
                player.start();
            } catch (IOException e) {
                Log.e(LOG_TAG, "prepare() failed");
            }
        }

        private void stopPlaying() {
            player.release();
            player = null;
        }

        private void startRecording() {
            recorder = new MediaRecorder();
            recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            recorder.setOutputFile(fileName);
            recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

            try {
                recorder.prepare();
            } catch (IOException e) {
                Log.e(LOG_TAG, "prepare() failed");
            }

            recorder.start();
        }

        private void stopRecording() {
            recorder.stop();
            recorder.release();
            recorder = null;
        }

        class RecordButton extends Button {
            boolean mStartRecording = true;

            OnClickListener clicker = new OnClickListener() {
                public void onClick(View v) {
                    onRecord(mStartRecording);
                    if (mStartRecording) {
                        setText("Stop recording");
                    } else {
                        setText("Start recording");
                    }
                    mStartRecording = !mStartRecording;
                }
            };

            public RecordButton(Context ctx) {
                super(ctx);
                setText("Start recording");
                setOnClickListener(clicker);
            }
        }

        class PlayButton extends Button {
            boolean mStartPlaying = true;

            OnClickListener clicker = new OnClickListener() {
                public void onClick(View v) {
                    onPlay(mStartPlaying);
                    if (mStartPlaying) {
                        setText("Stop playing");
                    } else {
                        setText("Start playing");
                    }
                    mStartPlaying = !mStartPlaying;
                }
            };

            public PlayButton(Context ctx) {
                super(ctx);
                setText("Start playing");
                setOnClickListener(clicker);
            }
        }

        @Override
        public void onCreate(Bundle icicle) {
            super.onCreate(icicle);

            // Record to the external cache directory for visibility
            fileName = getExternalCacheDir().getAbsolutePath();
            fileName += "/audiorecordtest.3gp";

            ActivityCompat.requestPermissions(this, permissions, REQUEST_RECORD_AUDIO_PERMISSION);

            LinearLayout ll = new LinearLayout(this);
            recordButton = new RecordButton(this);
            ll.addView(recordButton,
                    new LinearLayout.LayoutParams(
                            ViewGroup.LayoutParams.WRAP_CONTENT,
                            ViewGroup.LayoutParams.WRAP_CONTENT,
                            0));
            playButton = new PlayButton(this);
            ll.addView(playButton,
                    new LinearLayout.LayoutParams(
                            ViewGroup.LayoutParams.WRAP_CONTENT,
                            ViewGroup.LayoutParams.WRAP_CONTENT,
                            0));
            setContentView(ll);
        }

        @Override
        public void onStop() {
            super.onStop();
            if (recorder != null) {
                recorder.release();
                recorder = null;
            }

            if (player != null) {
                player.release();
                player = null;
            }
        }
    }
    

Saiba mais

Estas páginas abrangem temas relacionados à gravação, ao armazenamento e à reprodução de áudio e vídeo.