Présentation de MediaRecorder

Le framework multimédia Android prend en charge la capture et l'encodage de divers formats audio et vidéo courants. Vous pouvez utiliser les API MediaRecorder si elles sont compatibles avec le matériel de l'appareil.

Ce document explique comment utiliser MediaRecorder pour créer une application qui capture le son du micro d'un appareil, l'enregistrer et le lire (avec MediaPlayer). Pour enregistrer une vidéo, vous devez utiliser l'appareil photo de l'appareil avec MediaRecorder. Cette opération est décrite dans le guide de l'appareil photo.

Remarque:Android Emulator ne peut pas enregistrer de contenu audio. Veillez à tester votre code sur un appareil réel capable d'enregistrer.

Demande d'autorisation d'enregistrement audio

Pour pouvoir enregistrer, votre application doit indiquer à l'utilisateur qu'elle accédera à l'entrée audio de l'appareil. Vous devez inclure cette balise d'autorisation dans le fichier manifeste de l'application:

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

RECORD_AUDIO est considérée comme une autorisation"dangereuse", car elle peut présenter un risque pour la confidentialité de l'utilisateur. À partir d'Android 6.0 (niveau d'API 23), une application qui utilise une autorisation dangereuse doit demander l'approbation de l'utilisateur au moment de l'exécution. Une fois l'autorisation accordée par l'utilisateur, l'application doit s'en souvenir et ne plus redemander. L'exemple de code ci-dessous montre comment mettre en œuvre ce comportement à l'aide de ActivityCompat.requestPermissions().

Créer et exécuter un MediaRecorder

Initialisez une nouvelle instance de MediaRecorder avec les appels suivants:

  • Définissez la source audio à l'aide de setAudioSource(). Vous utiliserez probablement MIC.

    Remarque:La plupart des sources audio (y compris DEFAULT) appliquent un traitement au signal audio. Pour enregistrer du contenu audio brut, sélectionnez UNPROCESSED. Certains appareils ne sont pas compatibles avec les entrées non traitées. Appelez d'abord AudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) pour vérifier qu'elle est disponible. Si ce n'est pas le cas, essayez plutôt d'utiliser VOICE_RECOGNITION, qui n'utilise pas la récupération de mémoire ni la suppression du bruit. Vous pouvez utiliser UNPROCESSED comme source audio même lorsque la propriété n'est pas compatible, mais il n'y a aucune garantie que le signal ne sera pas traité ou non dans ce cas.

  • Définissez le format du fichier de sortie à l'aide de setOutputFormat(). À partir d'Android 8.0 (niveau d'API 26), MediaRecorder est compatible avec le format MPEG2_TS, qui est utile pour la diffusion en streaming:

    Kotlin

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
    

    Java

    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
    
  • Définissez le nom du fichier de sortie à l'aide de setOutputFile(). Vous devez spécifier un descripteur de fichier qui représente un fichier réel.
  • Définissez l'encodeur audio à l'aide de setAudioEncoder().
  • Terminez l'initialisation en appelant prepare().

Démarrez et arrêtez l'enregistreur en appelant respectivement start() et stop().

Une fois que vous avez terminé d'utiliser l'instance MediaRecorder, libérez ses ressources dès que possible en appelant release().

Remarque:Sur les appareils équipés d'Android 9 (niveau d'API 28) ou version ultérieure, les applications exécutées en arrière-plan ne peuvent pas accéder au micro. Par conséquent, votre application ne doit enregistrer du contenu audio que lorsqu'elle est exécutée au premier plan ou lorsque vous incluez une instance de MediaRecorder dans un service de premier plan.

Utiliser MediaMuxer pour enregistrer plusieurs canaux

À partir d'Android 8.0 (niveau d'API 26), vous pouvez utiliser un MediaMuxer pour enregistrer plusieurs flux audio et vidéo simultanés. Dans les versions antérieures d'Android, vous ne pouvez enregistrer qu'une piste audio et/ou une piste vidéo à la fois.

Utilisez la méthode addTrack() pour mélanger plusieurs pistes.

Vous pouvez également ajouter une ou plusieurs pistes de métadonnées avec des informations personnalisées pour chaque image, mais uniquement pour les conteneurs MP4. Votre application définit le format et le contenu des métadonnées.

Ajouter des métadonnées

Les métadonnées peuvent être utiles pour le traitement hors connexion. Par exemple, les données capturées à partir du capteur gyroscope peuvent être utilisées pour effectuer la stabilisation de la vidéo.

Lorsque vous ajoutez une piste de métadonnées, son format MIME doit commencer par le préfixe application/. L'écriture de métadonnées revient à écrire des données vidéo ou audio, sauf que les données ne proviennent pas d'un MediaCodec. À la place, l'application transmet un ByteBuffer avec un horodatage associé à la méthode writeSampleData(). Le code temporel doit correspondre à la même base de temps que les pistes vidéo et audio.

Le fichier MP4 généré utilise le TextMetaDataSampleEntry défini dans la section 12.3.3.2 de la spécification ISO BMFF pour signaler le format MIME des métadonnées. Lorsque vous utilisez un MediaExtractor pour extraire un fichier contenant des pistes de métadonnées, le format MIME des métadonnées apparaît en tant qu'instance de MediaFormat.

Exemple de code

L'exemple MediaRecorder montre comment créer un enregistrement vidéo à l'aide de MediaRecorder et de l'API Camera.

L'exemple d'activité ci-dessous montre comment utiliser MediaRecorder pour enregistrer un fichier audio. Il utilise également MediaPlayer pour lire le contenu 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;
        }
    }
}

En savoir plus

Ces pages couvrent des sujets liés à l'enregistrement, au stockage et à la lecture de contenus audio et vidéo.