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
。不過,應用程式會將
將 ByteBuffer
與
writeSampleData()
方法。
時間戳記必須與視訊軌和音軌的時間相符。
產生的 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; } } }
瞭解詳情
下列頁面說明與錄製、儲存和播放音訊及視訊相關的主題。