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 probablementMIC
.Remarque:La plupart des sources audio (y compris
DEFAULT
) appliquent un traitement au signal audio. Pour enregistrer du contenu audio brut, sélectionnezUNPROCESSED
. Certains appareils ne sont pas compatibles avec les entrées non traitées. Appelez d'abordAudioManager.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'utiliserVOICE_RECOGNITION
, qui n'utilise pas la récupération de mémoire ni la suppression du bruit. Vous pouvez utiliserUNPROCESSED
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.