O framework multimídia do Android inclui suporte para captura e codificação de vários formatos comuns
de áudio e vídeo. Você pode usar as APIs MediaRecorder
se o hardware do dispositivo oferecer suporte
a isso.
Este documento mostra como usar o MediaRecorder
para criar um aplicativo que captura áudio de um microfone
do dispositivo, salvar e reproduzir o áudio (com MediaPlayer
). Para gravar vídeos, você precisa
usar a câmera do dispositivo com o 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 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" />
A RECORD_AUDIO
é considerada uma
permissão "perigosa"
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 momento da execução. Depois que o usuário
concede a permissão, o app precisa se 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
Inicialize uma nova instância de MediaRecorder
com estas chamadas:
- Defina a fonte de áudio com
setAudioSource()
. Você provavelmente vai usarMIC
.Observação:a maioria das fontes de áudio (incluindo
DEFAULT
) aplica um processamento ao sinal de áudio. Para gravar áudio bruto, selecioneUNPROCESSED
. Alguns dispositivos não oferecem suporte a entradas não processadas. 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 emprega AGC nem supressão de ruído. Você pode usarUNPROCESSED
como uma fonte de áudio mesmo quando a propriedade não tiver suporte, mas não há garantia de que o sinal não será processado ou não nesse caso. - Defina o formato do arquivo de saída usando
setOutputFormat()
. A partir do Android 8.0 (API de nível 26),MediaRecorder
oferece suporte ao 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 usando
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 terminar de usar a instância MediaRecorder
, libere os recursos
o mais rápido possível chamando
release()
.
Observação:em dispositivos com o Android 9 (nível 28 da API) ou
versões mais recentes, os apps em execução em segundo plano não podem acessar o microfone. Portanto,
seu app precisa gravar áudio somente quando estiver em primeiro plano ou quando você
incluir 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 uma 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 combinar várias faixas.
Você também pode adicionar uma ou mais faixas de metadados com informações personalizadas para cada frame, mas somente a 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 de 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/
. Escrever metadados é o mesmo que gravar dados de vídeo ou áudio, com a diferença de que
os dados não vêm de um MediaCodec
. Em vez disso, o app transmite ao
método writeSampleData()
um
ByteBuffer
com um carimbo de data/hora associado.
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 formato MIME
dos metadados aparece como uma instância de MediaFormat
.
Exemplo de código
O exemplo do MediaRecorder 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 reproduzir 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.