Мультимедийная платформа Android поддерживает запись и кодирование в различных распространенных аудио- и видеоформатах. Вы можете использовать API MediaRecorder, если они поддерживаются аппаратным обеспечением устройства.
В этом документе рассказывается, как с помощью MediaRecorder написать приложение, которое записывает аудио с микрофона устройства, сохраняет его и воспроизводит (с помощью MediaPlayer). Чтобы записать видео, вам нужно будет использовать камеру устройства вместе с MediaRecorder. Подробную информацию вы найдете в руководстве по работе с камерой.
Примечание. Эмулятор Android не может записывать аудио. Протестируйте код на реальном устройстве, которое может записывать видео.
Запрос разрешения на запись аудио
Чтобы записывать звук, приложение должно сообщить пользователю, что оно получит доступ к аудиовходу устройства. В файл манифеста приложения необходимо добавить следующий тег разрешения:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Разрешение RECORD_AUDIO считается опасным, поскольку может представлять угрозу для конфиденциальности пользователя. Начиная с Android 6.0 (уровень API 23), приложение, использующее опасное разрешение, должно запрашивать у пользователя одобрение во время выполнения. После того как пользователь предоставит разрешение, приложение должно запомнить это и больше не запрашивать его. В приведенном ниже образце кода показано, как реализовать это поведение с помощью ActivityCompat.requestPermissions().
Создание и запуск MediaRecorder
Инициализируйте новый экземпляр MediaRecorder с помощью следующих вызовов:
- Установите источник аудио с помощью
setAudioSource(). Скорее всего, вы будете использоватьMIC.Примечание. Большинство источников аудио (включая
DEFAULT) применяют обработку к аудиосигналу. Чтобы записать необработанное аудио, выберитеUNPROCESSED. Некоторые устройства не поддерживают необработанные входные данные. Сначала позвоните по номеруAudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED), чтобы узнать, доступна ли эта услуга. Если это не так, попробуйте использоватьVOICE_RECOGNITION, в котором не применяется АРУ и шумоподавление. Вы можете использоватьUNPROCESSEDв качестве источника аудио, даже если свойство не поддерживается, но в этом случае нет гарантии, что сигнал будет необработанным. - Задайте формат выходного файла с помощью
setOutputFormat(). Обратите внимание, что начиная с Android 8.0 (уровень API 26)MediaRecorderподдерживает формат MPEG2_TS, который подходит для потоковой передачи:Kotlin
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
Java
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
- Задайте имя выходного файла с помощью
setOutputFile(). Необходимо указать дескриптор файла, который представляет фактический файл. - Задайте аудиокодер с помощью
setAudioEncoder(). - Завершите инициализацию, вызвав метод
prepare().
Запустите и остановите запись, вызвав методы start() и stop() соответственно.
Когда экземпляр MediaRecorder больше не нужен, освободите его ресурсы как можно скорее, вызвав release().
Примечание. На устройствах с Android 9 (уровень API 28) или более поздней версией приложения, работающие в фоновом режиме, не могут использовать микрофон. Поэтому приложение должно записывать аудио, только когда оно работает в активном режиме или когда вы включаете экземпляр MediaRecorder в активную службу.
Как использовать MediaMuxer для записи нескольких каналов
Начиная с Android 8.0 (уровень API 26) вы можете использовать MediaMuxer, чтобы записывать несколько аудио- и видеопотоков одновременно. В более ранних версиях Android можно записывать только одну аудио- и/или видеодорожку за раз.
Используйте метод addTrack(), чтобы смешать несколько дорожек.
Вы также можете добавить одну или несколько дорожек метаданных с информацией о каждом кадре, но только в контейнеры MP4. Формат и содержание метаданных определяются вашим приложением.
Добавление метаданных
Метаданные могут быть полезны для офлайн-обработки. Например, данные, полученные от гироскопа, могут использоваться для стабилизации видео.
При добавлении дорожки метаданных ее MIME-формат должен начинаться с префикса application/. Запись метаданных выполняется так же, как и запись видео- или аудиоданных, за исключением того, что данные не поступают из MediaCodec. Вместо этого приложение передает методу writeSampleData() объект ByteBuffer со связанной временной меткой.
Временная метка должна быть в той же временной базе, что и видео- и аудиодорожки.
В созданном файле MP4 используется TextMetaDataSampleEntry, определенный в разделе 12.3.3.2 спецификации ISO BMFF, чтобы указать MIME-формат метаданных. Если вы используете MediaExtractor для извлечения файла, содержащего дорожки метаданных, MIME-формат метаданных будет представлен как экземпляр MediaFormat.
Пример кода
Пример MediaRecorder демонстрирует, как записать видео с помощью MediaRecorder и Camera API.
В примере ниже показано, как использовать MediaRecorder для записи аудиофайла. Для воспроизведения аудио также используется MediaPlayer.
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; } } }
Подробнее…
На этих страницах рассматриваются темы, связанные с записью, хранением и воспроизведением аудио и видео.