กรอบการทำงานมัลติมีเดียของ Android ประกอบด้วยการสนับสนุนการบันทึกและการเข้ารหัส
ของเสียงหรือวิดีโอ คุณสามารถใช้ API ของ MediaRecorder
ได้หากรองรับ
ตามฮาร์ดแวร์อุปกรณ์
เอกสารนี้แสดงวิธีใช้ MediaRecorder
เพื่อเขียนแอปพลิเคชันที่บันทึกเสียงจากอุปกรณ์
ไมโครโฟน บันทึกเสียง และเล่นเสียง (ด้วย MediaPlayer
) ในการบันทึกวิดีโอ คุณต้องทำดังนี้
ใช้กล้องของอุปกรณ์ร่วมกับ MediaRecorder
ซึ่งอธิบายไว้ในคู่มือกล้อง
หมายเหตุ: โปรแกรมจำลองของ Android บันทึกไม่ได้ เสียง อย่าลืมทดสอบโค้ดบนอุปกรณ์จริงที่สามารถบันทึกได้
กำลังขออนุญาตบันทึกเสียง
แอปต้องแจ้งให้ผู้ใช้ทราบว่าจะเข้าถึง อินพุตเสียงของอุปกรณ์ คุณต้องรวมแท็กสิทธิ์นี้ในไฟล์ Manifest ของแอป ดังนี้
<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 เวอร์ชันก่อนหน้า คุณจะทำได้เพียง
บันทึกแทร็กเสียงและ/หรือแทร็กวิดีโอครั้งละ 1 แทร็ก
ใช้ addTrack()
ในการผสมผสานหลายๆ แทร็กเข้าด้วยกัน
คุณยังสามารถเพิ่มแทร็กข้อมูลเมตาได้อย่างน้อยหนึ่งแทร็กที่มีข้อมูลที่กำหนดเองสำหรับแต่ละเฟรม แต่ กับคอนเทนเนอร์ MP4 เท่านั้น แอปของคุณกำหนดรูปแบบและเนื้อหาของข้อมูลเมตา
การเพิ่มข้อมูลเมตา
ข้อมูลเมตาอาจเป็นประโยชน์ต่อการประมวลผลแบบออฟไลน์ เช่น ข้อมูลที่บันทึกจาก สามารถใช้เซ็นเซอร์ไจโรเพื่อกันภาพวิดีโอสั่นได้
เมื่อคุณเพิ่มแทร็กข้อมูลเมตา รูปแบบ MIME ของแทร็กต้องขึ้นต้นด้วยคำนำหน้า
application/
การเขียนข้อมูลเมตาเหมือนกับการเขียนข้อมูลวิดีโอหรือเสียง ยกเว้น
ว่าข้อมูลไม่ได้มาจาก MediaCodec
แต่แอปจะส่ง
ByteBuffer
ที่มีการประทับเวลาที่เชื่อมโยงกับ
writeSampleData()
วิธี
การประทับเวลาต้องอยู่ในฐานเวลาเดียวกันกับแทร็กวิดีโอและแทร็กเสียง
ไฟล์ 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; } } }
ดูข้อมูลเพิ่มเติม
หน้าเหล่านี้ครอบคลุมหัวข้อต่างๆ ที่เกี่ยวข้องกับการบันทึก การจัดเก็บ และการเล่นเสียงและวิดีโอ