Descripción general de MediaRecorder

El framework multimedia de Android admite la captura y codificación de una variedad de formatos de audio y video. Puedes usar las APIs de MediaRecorder si son compatibles el hardware del dispositivo.

En este documento, se muestra cómo usar MediaRecorder para escribir una app que capture audio de un dispositivo micrófono, guarda el audio y reprodúcelo de nuevo (con MediaPlayer). Para grabar un video, deberás usa la cámara del dispositivo junto con MediaRecorder. Esto se describe en la guía de la cámara.

Nota: Android Emulator no puede grabar. audio. Asegúrate de probar el código en un dispositivo real que pueda grabar.

Cómo solicitar permiso para grabar audio

Para poder grabar, la app debe indicarle al usuario que accederá al entrada de audio del dispositivo. Tienes que incluir esta etiqueta de permiso en el archivo de manifiesto de la app:

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

RECORD_AUDIO se considera un “peligroso” permiso porque puede poner en riesgo la privacidad del usuario. A partir de Android 6.0 (nivel de API 23), una app que use un permiso riesgoso debe solicitar la aprobación del usuario en el tiempo de ejecución. Una vez que el usuario se le otorgó el permiso, la app debe recordarlo y no volver a preguntar. En el siguiente código de muestra, se indica cómo implementar este comportamiento ActivityCompat.requestPermissions()

Cómo crear y ejecutar un MediaRecorder

Inicializa una instancia nueva de MediaRecorder. con las siguientes llamadas:

  • Configura la fuente de audio con setAudioSource() Deberás probablemente uses MIC.

    Nota: La mayoría de las fuentes de audio (incluido DEFAULT) procesan el archivo. señal de audio. Para grabar audio sin procesar, selecciona UNPROCESSED Algunos dispositivos no admiten entrada. Primero, llama al AudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) para verificar que esté disponible. Si no es así, intenta usar VOICE_RECOGNITION en su lugar. que no emplea AGC ni supresión de ruido. Puedes usar UNPROCESSED como fuente de audio incluso cuando la propiedad no es compatible, pero no hay garantía de que En ese caso, la señal no se procesará.

  • Configura el formato del archivo de salida con setOutputFormat() Ten en cuenta que, a partir de Android 8.0 (nivel de API 26), MediaRecorder admite el protocolo de transmisión, que es útil para transmitir:

    Kotlin

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
    

    Java

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
    
  • Establece el nombre del archivo de salida usando setOutputFile() Tienes que especificar un descriptor del archivo que represente un archivo real.
  • Configura el codificador de audio con setAudioEncoder()
  • Completa la inicialización llamando a prepare()

Llama para iniciar y detener la grabadora start() y stop() respectivamente.

Cuando termines de usar la instancia MediaRecorder, libera sus recursos lo antes posible llamando release()

Nota: En dispositivos que ejecutan Android 9 (nivel de API 28) o las apps que se ejecutan en segundo plano no pueden acceder al micrófono. Por lo tanto, tu app debe grabar audio solo cuando esté en primer plano o cuando incluye una instancia de MediaRecorder en un servicio en primer plano.

Cómo usar MediaMuxer para grabar varios canales

A partir de Android 8.0 (nivel de API 26), puedes usar un MediaMuxer para grabar varias transmisiones simultáneas de audio y video. En versiones anteriores de Android, solo podías grabar una pista de audio o una pista de video a la vez.

Usa el addTrack() para mezclar varias pistas.

También puedes agregar una o más pistas de metadatos con información personalizada para cada fotograma, pero solo a contenedores MP4. La app define el formato y el contenido de los metadatos.

Cómo agregar metadatos

Los metadatos pueden ser útiles para el procesamiento sin conexión. Por ejemplo, los datos capturados de usa el sensor giroscópico para estabilizar video.

Cuando agregas una pista de metadatos, el formato MIME de la pista debe comenzar con el prefijo application/ Escribir metadatos es lo mismo que escribir datos de video o audio, excepto que los datos no provienen de una MediaCodec. En cambio, la app pasa una ByteBuffer por una marca de tiempo asociada al writeSampleData(). La marca de tiempo tiene que estar en la misma base de tiempo que las pistas de video y audio.

El archivo MP4 generado usa el TextMetaDataSampleEntry definido en la sección 12.3.3.2 de la especificación ISO BMFF para indicar el formato MIME de los metadatos. Cuando usas un MediaExtractor para extraer un archivo que contiene pistas de metadatos, el MIME de los metadatos aparece como una instancia de MediaFormat.

Código de muestra

La función MediaRecorder En este ejemplo, se muestra cómo realizar una grabación de video con MediaRecorder y la API de cámara.

En la siguiente actividad de ejemplo, se muestra cómo usar MediaRecorder para grabar un archivo de audio. Integra También usa MediaPlayer para reproducir el audio.

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;
        }
    }
}

Más información

En estas páginas, se analizan temas relacionados con la grabación, el almacenamiento y la reproducción de audio y video.