Ringkasan MediaRecorder

Framework multimedia Android menyertakan dukungan untuk merekam dan mengenkode berbagai format audio dan video umum. Anda dapat menggunakan API MediaRecorder jika didukung oleh hardware perangkat.

Dokumen ini menunjukkan cara menggunakan MediaRecorder untuk menulis aplikasi yang merekam audio dari mikrofon perangkat, menyimpan audio, dan memutarnya kembali (dengan MediaPlayer). Untuk merekam video, Anda harus menggunakan kamera perangkat beserta . 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 akan mengakses masukan audio perangkat. Anda harus menyertakan tag izin ini dalam file manifes aplikasi:

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

RECORD_AUDIO dianggap sebagai izin "berbahaya", karena dapat menimbulkan risiko bagi privasi pengguna. Mulai Android 6.0 (API level 23), aplikasi yang menggunakan izin berbahaya harus meminta persetujuan pengguna saat waktu proses. Setelah pengguna memberikan izin, aplikasi akan mengingat dan tidak bertanya lagi. Kode contoh di bawah ini menunjukkan cara menerapkan perilaku ini menggunakan ActivityCompat.requestPermissions().

Membuat dan menjalankan MediaRecorder

Menginisialisasi instance MediaRecorder baru dengan panggilan berikut:

  • Setel sumber audio menggunakan setAudioSource(). Anda mungkin akan menggunakan MIC.

    Catatan: Sebagian besar sumber audio (termasuk DEFAULT) menerapkan pemrosesan ke sinyal audio. Untuk merekam audio mentah, pilih UNPROCESSED. Beberapa perangkat tidak mendukung masukan yang tidak diproses. Panggil AudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) terlebih dahulu untuk memverifikasi bahwa aplikasi tersedia. Jika tidak, coba gunakan VOICE_RECOGNITION sebagai gantinya, yang tidak menggunakan AGC atau peredam bising. Anda dapat menggunakan UNPROCESSED sebagai sumber audio meskipun properti tidak didukung, tetapi tidak ada jaminan apakah sinyal akan diproses atau tidak dalam kasus tersebut.

  • Tetapkan format file keluaran menggunakan setOutputFormat(). Perhatikan bahwa mulai Android 8.0 (API level 26) MediaRecorder mendukung format MPEG2_TS, yang berguna untuk streaming:

    Kotlin

        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
        

    Java

        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
        
  • Tetapkan nama file keluaran 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 dengan memanggil start() dan stop().

Setelah selesai membuat dan menjalankan instance MediaRecorder, bebaskan resource-nya sesegera mungkin dengan memanggil release().

Catatan: Pada perangkat yang menjalankan Android 9 (API level 28) atau yang 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. Pada versi Android yang lebih lama, Anda hanya dapat merekam satu trek audio dan/atau satu trek video dalam satu waktu.

Gunakan metode addTrack() untuk menggabungkan beberapa trek menjadi satu.

Anda juga dapat menambahkan satu atau beberapa trek metadata dengan informasi khusus untuk setiap frame, tetapi hanya untuk penampung 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 meneruskan ByteBuffer dengan stempel waktu terkait ke metode writeSampleData(). Stempel waktu harus berada dalam basis waktu yang sama dengan trek video dan audio.

File MP4 yang dihasilkan menggunakan TextMetaDataSampleEntry yang ditetapkan dalam bagian 12.3.3.2 dari spesifikasi ISO BMFF untuk menandakan format mime metadata. Saat Anda menggunakan MediaExtractor untuk mengekstrak file yang berisi trek metadata, format mime metadata muncul sebagai instance MediaFormat.

Kode contoh

Contoh MediaRecorder menunjukkan cara membuat rekaman video menggunakan MediaRecorder dan Camera API.

Aktivitas contoh di bawah ini menunjukkan cara menggunakan MediaRecorder untuk merekam file audio. MediaPlayer juga digunakan 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;
            }
        }
    }
    

Mempelajari lebih lanjut

Halaman ini membahas topik yang berkaitan dengan perekaman, penyimpanan, dan pemutaran kembali audio dan video.