چارچوب چندرسانهای Android شامل پشتیبانی از ضبط و کدبندی انواع قالبهای رایج صوتی و ویدیویی است. اگر سختافزار دستگاه پشتیبانی کند، میتوانید از میاناهای برنامهسازی کاربردی MediaRecorder استفاده کنید.
این سند به شما نشان میدهد که چگونه از MediaRecorder برای نوشتن برنامهای استفاده کنید که صدا را از میکروفون دستگاه ضبط میکند، صدا را ذخیره میکند، و آن را بازپخش میکند (با MediaPlayer). برای ضبط ویدیو باید از دوربین دستگاه بههمراه MediaRecorder استفاده کنید. این موضوع در راهنمای دوربین شرح داده شده است.
توجه: «شبیهساز Android» نمیتواند صدا ضبط کند. حتماً کدتان را در دستگاه واقعی که میتواند ضبط کند آزمایش کنید.
درخواست اجازه برای ضبط صدا
برای اینکه برنامه شما بتواند ضبط کند، باید به کاربر اطلاع دهد که به ورودی صدای دستگاه دسترسی خواهد داشت. باید این برچسب اجازه را در فایل مانیفست برنامه لحاظ کنید:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
RECORD_AUDIO
اجازه «خطرناک» درنظر گرفته میشود
چون ممکن است حریم خصوصی کاربر را بهخطر بیندازد. از Android نسخه ۶.۰ (سطح میانای برنامهسازی کاربردی ۲۳) بهبعد، برنامهای که از اجازه خطرناک استفاده میکند باید در زمان اجرا از کاربر تأییدیه بگیرد. پساز اینکه کاربر اجازه را اعطا کرد، برنامه باید آن را بهخاطر بسپارد و دوباره نپرسد. کد نمونه زیر نشان میدهد که چگونه
این رفتار را بااستفاده از
ActivityCompat.requestPermissions() پیادهسازی کنید.
ایجاد و اجرای MediaRecorder
نمونه جدیدی از MediaRecorder را با تماسهای زیر مقداردهی اولیه کنید:
- منبع صوتی را بااستفاده از
setAudioSource()تنظیم کنید. احتمالاً ازMICاستفاده خواهید کرد.توجه: اکثر منابع صوتی (ازجمله
DEFAULT) پردازش را روی سیگنال صوتی اعمال میکنند. برای ضبط صدای خام،UNPROCESSEDرا انتخاب کنید. برخیاز دستگاهها از ورودی پردازشنشده پشتیبانی نمیکنند. ابتدا باAudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED)تماس بگیرید تا مطمئن شوید دردسترس است. اگر اینطور نیست، بهجای آن ازVOICE_RECOGNITIONاستفاده کنید، که از AGC یا نوفهزدایی استفاده نمیکند. میتوانید ازUNPROCESSEDبهعنوان منبع صوتی استفاده کنید، حتی زمانیکه این ویژگی پشتیبانی نمیشود، اما در این حالت تضمینی وجود ندارد که سیگنال پردازشنشده باشد یا نه. - قالب فایل خروجی را بااستفاده از
setOutputFormat()تنظیم کنید. توجه داشته باشید که از Android 8.0 (سطح میانای برنامهسازی کاربردی ۲۶) به بعد،MediaRecorderاز قالب MPEG2_TS پشتیبانی میکند که برای جاریسازی مفید است:کاتلین
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
جاوا
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
- نام فایل خروجی را بااستفاده از
setOutputFile()تنظیم کنید. باید توصیفگر فایلی را مشخص کنید که نشاندهنده فایل واقعی باشد. - کدبند صوتی را بااستفاده از
setAudioEncoder()تنظیم کنید. - با تماس با
prepare()، مقداردهی اولیه را تکمیل کنید.
با فراخوانی
start() و
stop()
بهترتیب ضبطکننده را شروع و متوقف کنید.
وقتی کارتان با نمونه MediaRecorder تمام شد،
در اسرع وقت با فراخوانی release()
منابع آن را آزاد کنید.
توجه: در دستگاههای دارای Android نسخه ۹ (سطح API ۲۸) یا
بالاتر، برنامههایی که در پسزمینه اجرا میشوند نمیتوانند به میکروفون دسترسی داشته باشند. بنابراین،
برنامه شما باید فقط زمانی صدا ضبط کند که در پیشنما باشد یا زمانیکه
نمونهای از MediaRecorder را در
سرویس پیشزمینهای قرار دهید.
استفاده از MediaMuxer برای ضبط چند کانال
از Android 8.0 (سطح API ۲۶) به بعد، میتوانید از MediaMuxer
برای ضبط چندین جاریسازی همزمان صدا و ویدیو استفاده کنید. در نسخههای قدیمیتر Android، هربار فقط میتوانید
یک قطعه صوتی و/یا یک قطعه ویدیویی ضبط کنید.
از روش addTrack()
برای ترکیب کردن چند قطعه باهم استفاده کنید.
همچنین میتوانید یک یا چند قطعه فراداده با اطلاعات سفارشی برای هر فریم اضافه کنید، اما فقط به محفظههای MP4. برنامه شما قالب و محتوای فراداده را تعریف میکند.
درحال افزودن فراداده
فراداده میتواند برای پردازش آفلاین مفید باشد. برای مثال، دادههای ضبطشده از حسگر ژیروسکوپ میتواند برای تثبیت ویدیو استفاده شود.
وقتی قطعه فراداده اضافه میکنید، قالب mime قطعه باید با پیشوند
application/ شروع شود. نوشتن فراداده مانند نوشتن دادههای ویدیویی یا صوتی است، با این تفاوت که
دادهها از MediaCodec نمیآیند. درعوض، برنامه یک
ByteBuffer با مُهر زمان مرتبط به روش
writeSampleData() ارسال میکند.
مُهر زمان باید در همان پایه زمانی قطعههای ویدیویی و صوتی باشد.
فایل MP4 تولیدشده از TextMetaDataSampleEntry تعریفشده در بخش ۱۲.۳.۳.۲
از مشخصات ISO BMFF
برای نشان دادن قالب mime فراداده استفاده میکند. وقتی از MediaExtractor برای استخراج فایلی که حاوی قطعههای فراداده است استفاده میکنید، قالب mime فراداده بهصورت نمونهای از MediaFormat نشان داده میشود.
کدگذاری نمونه
نمونه MediaRecorder نشان میدهد که چگونه بااستفاده از MediaRecorder و Camera API، ویدیو ضبط کنید.
فعالیت نمونه زیر نحوه استفاده از MediaRecorder را برای ضبط فایل صوتی نشان میدهد. همچنین
از MediaPlayer برای بازپخش صدا استفاده میکند.
کاتلین
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 } }
جاوا
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; } } }
بیشتر بدانید
این صفحهها موضوعات مربوط به ضبط، ذخیره، و بازپخش صدا و ویدیو را پوشش میدهند.