Panoramica di MediaRecorder

Il framework multimediale Android include il supporto per l'acquisizione e la codifica di una serie di formati audio e video comuni. Puoi utilizzare le API MediaRecorder, se supportate dall'hardware del dispositivo.

Questo documento mostra come utilizzare MediaRecorder per scrivere un'applicazione che acquisisca l'audio dal microfono del dispositivo, salvare l'audio e riprodurlo (con MediaPlayer). Per registrare un video, devi utilizzare la videocamera del dispositivo insieme a MediaRecorder. come descritto nella guida Fotocamera.

Nota: l'emulatore Android non può registrare audio. Assicurati di testare il codice su un dispositivo reale in grado di registrare.

Richiesta dell'autorizzazione per registrare audio

Per poter registrare, l'app deve comunicare all'utente che accederà all'input audio del dispositivo. Devi includere questo tag di autorizzazione nel file manifest dell'app:

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

RECORD_AUDIO è considerata un'autorizzazione "pericolosa" perché potrebbe rappresentare un rischio per la privacy dell'utente. A partire da Android 6.0 (livello API 23), un'app che utilizza un'autorizzazione pericolosa deve chiedere all'utente l'approvazione in fase di esecuzione. Una volta che l'utente ha concesso l'autorizzazione, l'app dovrebbe ricordarla e non chiedermelo più. Il codice di esempio riportato di seguito mostra come implementare questo comportamento utilizzando ActivityCompat.requestPermissions().

Creazione ed esecuzione di MediaRecorder

Inizializza una nuova istanza di MediaRecorder con le seguenti chiamate:

  • Imposta la sorgente audio utilizzando setAudioSource(). Probabilmente utilizzerai MIC.

    Nota: la maggior parte delle sorgenti audio (tra cui DEFAULT) applica l'elaborazione al segnale audio. Per registrare l'audio non elaborato, seleziona UNPROCESSED. Alcuni dispositivi non supportano l'input non elaborato. Chiama prima AudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) per verificare che sia disponibile. In caso contrario, prova a utilizzare VOICE_RECOGNITION, che non utilizza AGC o la soppressione del rumore. Puoi utilizzare UNPROCESSED come sorgente audio anche quando la proprietà non è supportata, ma in questo caso non vi è alcuna garanzia che il segnale non venga elaborato o meno.

  • Imposta il formato del file di output utilizzando setOutputFormat(). Tieni presente che a partire da Android 8.0 (livello API 26) MediaRecorder supporta il formato MPEG2_TS, utile per lo streaming:

    Kotlin

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
    

    Java

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
    
  • Imposta il nome del file di output utilizzando setOutputFile(). Devi specificare un descrittore di file che rappresenti un file effettivo.
  • Imposta il codificatore audio utilizzando setAudioEncoder().
  • Completa l'inizializzazione chiamando prepare().

Avvia e interrompi il registratore chiamando rispettivamente start() e stop().

Al termine dell'utilizzo dell'istanza MediaRecorder, libera le risorse il prima possibile chiamando release().

Nota: sui dispositivi con Android 9 (livello API 28) o versioni successive, le app eseguite in background non possono accedere al microfono. Di conseguenza, la tua app deve registrare l'audio solo quando viene mostrato in primo piano o quando includi un'istanza di MediaRecorder in un servizio in primo piano.

Utilizzare MediaMuxer per registrare più canali

A partire da Android 8.0 (livello API 26) puoi utilizzare MediaMuxer per registrare più stream audio e video simultanei. Nelle versioni precedenti di Android era possibile registrare una sola traccia audio e/o una traccia video alla volta.

Usa il metodo addTrack() per mixare più tracce.

Puoi anche aggiungere una o più tracce di metadati con informazioni personalizzate per ogni frame, ma solo ai container MP4. L'app definisce il formato e i contenuti dei metadati.

Aggiunta di metadati

I metadati possono essere utili per l'elaborazione offline. Ad esempio, i dati acquisiti dal sensore del giroscopio potrebbero essere utilizzati per eseguire la stabilizzazione video.

Quando aggiungi una traccia di metadati, il formato MIME della traccia deve iniziare con il prefisso application/. Scrivere metadati è come scrivere dati video o audio, ma i dati non provengono da un MediaCodec. Invece, l'app trasmette un ByteBuffer con un timestamp associato al metodo writeSampleData(). Il timestamp deve coincidere con la stessa base temporale delle tracce video e audio.

Il file MP4 generato utilizza il valore TextMetaDataSampleEntry definito nella sezione 12.3.3.2 della specifica ISO BMFF per segnalare il formato MIME dei metadati. Quando utilizzi un MediaExtractor per estrarre un file che contiene tracce di metadati, il formato MIME dei metadati viene visualizzato come istanza di MediaFormat.

Codice di esempio

L'esempio di MediaRecorder mostra come effettuare una registrazione video utilizzando MediaRecorder e l'API Camera.

La seguente attività di esempio mostra come utilizzare MediaRecorder per registrare un file audio. Utilizza anche MediaPlayer per riprodurre l'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;
        }
    }
}

Scopri di più

Queste pagine trattano argomenti relativi alla registrazione, all'archiviazione e alla riproduzione di audio e video.