Kerangka kerja multimedia Android menyertakan dukungan untuk merekam dan mengenkode berbagai
format audio dan video. Anda dapat menggunakan API MediaRecorder
jika didukung
oleh hardware perangkat.
Dokumen ini menunjukkan cara menggunakan MediaRecorder
untuk menulis aplikasi yang merekam audio dari perangkat
mikrofon, simpan audio, dan putar kembali (dengan MediaPlayer
). Untuk merekam video, Anda harus
menggunakan kamera perangkat beserta MediaRecorder
. Hal ini dijelaskan dalam panduan Kamera.
Catatan: Android Emulator tidak dapat merekam audio. Pastikan untuk menguji kode Anda pada perangkat sungguhan yang dapat merekam.
Meminta izin untuk merekam audio
Agar dapat merekam, aplikasi Anda harus memberi tahu pengguna bahwa ia akan mengakses input audio perangkat. Anda harus menyertakan tag izin ini dalam file manifes aplikasi:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
RECORD_AUDIO
dianggap sebagai
"berbahaya" izin
karena dapat menimbulkan
risiko bagi privasi pengguna. Mulai Android 6.0 (API level 23) aplikasi
yang menggunakan izin berbahaya harus meminta persetujuan pengguna saat runtime. Setelah pengguna memiliki
memberikan izin, aplikasi harus mengingat dan tidak meminta lagi. Kode contoh di bawah ini menunjukkan cara
menerapkan perilaku ini menggunakan
ActivityCompat.requestPermissions()
.
Membuat dan menjalankan MediaRecorder
Melakukan inisialisasi instance baru MediaRecorder
dengan panggilan berikut:
- Setel sumber audio menggunakan
setAudioSource()
. Anda akan mungkin menggunakanMIC
.Catatan: Sebagian besar sumber audio (termasuk
DEFAULT
) menerapkan pemrosesan ke sinyal audio. Untuk merekam audio mentah, pilihUNPROCESSED
. Beberapa perangkat tidak mendukung proses yang belum diproses input teks. PanggilAudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED)
terlebih dahulu untuk memverifikasi bahwa aplikasi tersedia. Jika bukan, coba gunakanVOICE_RECOGNITION
, yang tidak menggunakan AGC atau peredam bising. Anda dapat menggunakanUNPROCESSED
sebagai sumber audio meskipun properti tidak didukung, tetapi tidak ada jaminan apakah sinyalnya tidak akan diproses atau dalam kasus tersebut. - Setel format file output menggunakan
setOutputFormat()
. Perhatikan bahwa mulai Android 8.0 (level API 26),MediaRecorder
mendukung MPEG2_TS , yang berguna untuk streaming:Kotlin
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
Java
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
- Setel nama file {i>output<i} menggunakan
setOutputFile()
. Anda harus menentukan deskripsi file yang mewakili file yang sebenarnya. - Setel encoder audio menggunakan
setAudioEncoder()
. - Selesaikan inisialisasi dengan memanggil
prepare()
.
Mulai dan hentikan perekam suara dengan memanggil
start()
dan
stop()
secara berurutan.
Setelah selesai dengan instance MediaRecorder
, kosongkan resource-nya
sesegera mungkin dengan memanggil
release()
.
Catatan: Pada perangkat yang menjalankan Android 9 (API level 28) atau
lebih tinggi, aplikasi yang berjalan di latar belakang tidak dapat mengakses mikrofon. Oleh karena itu,
aplikasi Anda sebaiknya hanya merekam audio saat berada di latar depan atau saat Anda
menyertakan instance MediaRecorder
dalam
layanan latar depan.
Menggunakan MediaMuxer untuk merekam beberapa saluran
Mulai Android 8.0 (API level 26), Anda dapat menggunakan MediaMuxer
untuk merekam beberapa streaming audio dan video secara bersamaan. Di versi Android sebelumnya, Anda hanya dapat
merekam satu trek audio dan/atau satu trek video pada satu waktu.
Menggunakan addTrack()
untuk mencampur beberapa trek.
Anda juga dapat menambahkan satu atau beberapa trek metadata dengan informasi khusus untuk setiap {i>frame<i}, tetapi hanya untuk kontainer MP4. Aplikasi Anda menentukan format dan konten metadata.
Menambahkan metadata
Metadata dapat berguna untuk pemrosesan secara offline. Misalnya, data yang diambil dari sensor giroskop dapat digunakan untuk melakukan stabilisasi video.
Saat Anda menambahkan trek metadata, format mime trek harus diawali dengan awalan
application/
. Menulis metadata sama seperti menulis data video atau audio, kecuali
bahwa data tidak berasal dari MediaCodec
. Sebagai gantinya, aplikasi ini meneruskan
ByteBuffer
dengan stempel waktu yang terkait dengan
Metode writeSampleData()
.
Stempel waktu harus berada dalam basis waktu yang sama dengan trek video dan audio.
File MP4 yang dihasilkan menggunakan TextMetaDataSampleEntry
yang ditentukan di bagian 12.3.3.2
dari spesifikasi ISO BMFF
untuk memberi sinyal
format mime metadata. Saat Anda menggunakan MediaExtractor
untuk mengekstrak file yang berisi trek metadata, mime metadata
muncul sebagai instance dari MediaFormat
.
Kode contoh
MediaRecorder contoh menunjukkan cara membuat rekaman video menggunakan MediaRecorder dan Camera API.
Aktivitas contoh di bawah ini menunjukkan cara menggunakan MediaRecorder
untuk merekam file audio. Ini
Juga menggunakan MediaPlayer
untuk memutar kembali 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; } } }
Pelajari lebih lanjut
Halaman ini membahas topik yang berkaitan dengan perekaman, penyimpanan, dan pemutaran kembali audio dan video.