Visão geral do MediaRecorder

O framework multimídia do Android inclui suporte para capturar e codificar diversos tipos formatos de áudio e vídeo. É possível usar as APIs MediaRecorder, se houver suporte pelo hardware do dispositivo.

Este documento mostra como usar o MediaRecorder para criar um aplicativo que captura áudio de um dispositivo microfone, salvar o áudio e reproduzi-lo (com MediaPlayer). Para gravar um vídeo, você precisa use a câmera do dispositivo com MediaRecorder. 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 gravar, seu app precisa informar ao usuário que acessará o na 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 é considerado um "perigoso" permissão porque pode representar um risco à privacidade do usuário. A partir do Android 6.0 (nível 23 da API), um app que usa uma permissão perigosa precisa solicitar a aprovação do usuário no ambiente de execução. Depois que o usuário recebeu permissão, o app deve lembrar e não perguntar novamente. O exemplo de código abaixo mostra como implementar esse comportamento usando ActivityCompat.requestPermissions():

Como criar e executar um MediaRecorder

Inicializar uma nova instância de MediaRecorder com estas chamadas:

  • Definir a fonte de áudio usando setAudioSource(): Você vai provavelmente usam MIC.

    Observação:a maioria das fontes de áudio (incluindo DEFAULT) aplica processamento aos sinal de áudio. Para gravar áudio RAW, selecione UNPROCESSED: Alguns dispositivos não oferecem suporte entrada. 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 usa AGC nem supressão de ruído. Você pode usar UNPROCESSED como fonte de áudio, mesmo quando a propriedade não tem suporte, mas não há garantia de que nesse caso, o sinal não será processado ou não.

  • Defina o formato do arquivo de saída usando setOutputFormat(): Observe que, a partir do Android 8.0 (nível 26 da API), MediaRecorder oferece suporte à API MPEG2_TS, o 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 usando setOutputFile(): É necessário especificar um descritor de arquivo que represente um arquivo real.
  • Configure o codificador de áudio usando setAudioEncoder():
  • Conclua a inicialização chamando prepare():

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

Quando você terminar de usar a instância MediaRecorder, libere os recursos dela o mais rápido possível ligando release().

Observação:em dispositivos com o Android 9 (nível 28 da API) ou superior, os aplicativos executados em segundo plano não poderão acessar o microfone. Portanto, seu app deve gravar áudio somente quando estiver em primeiro plano ou quando você inclua uma instância de MediaRecorder em um serviço em primeiro plano.

Como usar o MediaMuxer para gravar vários canais

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

Usar a 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 a contêineres 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, dados coletados de o sensor giroscópio pode ser usado para estabilizar o vídeo.

Quando você adiciona uma faixa de metadados, o formato MIME da faixa deve começar com o prefixo application/: Gravar metadados é o mesmo que gravar dados de vídeo ou áudio, exceto pelo que os dados não sejam provenientes de um MediaCodec. Em vez disso, o aplicativo passa uma ByteBuffer pelo carimbo de data/hora associado à 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 para sinalizar o formato MIME dos metadados. Quando você usa um MediaExtractor para extrair um arquivo que contém faixas de metadados, o objeto MIME dos metadados aparece como uma instância de MediaFormat.

Exemplo de código

O MediaRecorder exemplo 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 MediaPlayer para tocar o áudio.

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

Essas páginas abrangem tópicos relacionados à gravação, ao armazenamento e à reprodução de áudio e vídeo.