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 usamMIC
.Observação:a maioria das fontes de áudio (incluindo
DEFAULT
) aplica processamento aos sinal de áudio. Para gravar áudio RAW, selecioneUNPROCESSED
: Alguns dispositivos não oferecem suporte entrada. Primeiro, chameAudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED)
para confirmar que ele está disponível. Se não estiver, tente usarVOICE_RECOGNITION
. que não usa AGC nem supressão de ruído. Você pode usarUNPROCESSED
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.
- Formatos de mídia compatíveis
- Solicitar permissões
- Armazenamento de dados
- MediaPlayer (link em inglês)