MediaRecorder 總覽

Android 多媒體架構支援各種常見程式的擷取和編碼作業 像是音訊和影片格式等如果支援,您可以使用 MediaRecorder API 由裝置硬體執行

本文件說明如何使用 MediaRecorder 編寫可從裝置擷取音訊的應用程式 麥克風,儲存音訊並重播 (使用 MediaPlayer)。如要錄影,請執行以下動作: 使用裝置攝影機和「MediaRecorder」。相關說明請參閱相機指南。

注意:Android Emulator 無法錄製 音訊。務必使用可錄製的實際裝置測試程式碼。

正在要求錄音權限

如要錄影,應用程式必須告知使用者會存取 裝置的音訊輸入裝置。您必須在應用程式的資訊清單檔案中加入這項權限標記:

<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 此模型並未採用 AGC 或雜訊抑制您可以使用「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。不過,應用程式會將 將 ByteBufferwriteSampleData() 方法。 時間戳記必須與視訊軌和音軌的時間相符。

產生的 MP4 檔案會使用第 12.3.3.2 節中定義的 TextMetaDataSampleEntry 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;
        }
    }
}

瞭解詳情

下列頁面說明與錄製、儲存和播放音訊及視訊相關的主題。