چارچوب چند رسانه ای اندروید شامل پشتیبانی از ضبط و کدگذاری انواع فرمت های رایج صوتی و تصویری است. اگر توسط سخت افزار دستگاه پشتیبانی می شود، می توانید از API های MediaRecorder استفاده کنید.
این سند به شما نشان می دهد که چگونه از MediaRecorder برای نوشتن برنامه ای استفاده کنید که صدا را از میکروفون دستگاه ضبط می کند، صدا را ذخیره می کند و آن را پخش می کند (با MediaPlayer ). برای ضبط ویدیو باید از دوربین دستگاه به همراه MediaRecorder استفاده کنید. این در راهنمای دوربین توضیح داده شده است.
توجه: شبیه ساز اندروید نمی تواند صدا را ضبط کند. حتما کد خود را روی یک دستگاه واقعی که قابلیت ضبط را دارد تست کنید.
درخواست مجوز برای ضبط صدا
برای اینکه بتوانید ضبط کنید، برنامه شما باید به کاربر بگوید که به ورودی صوتی دستگاه دسترسی خواهد داشت. باید این تگ مجوز را در فایل مانیفست برنامه قرار دهید:
<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()تنظیم کنید. توجه داشته باشید کهMediaRecorderبا Android 8.0 (سطح API 26) از فرمت MPEG2_TS پشتیبانی می کند که برای پخش جریانی مفید است:کاتلین
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
جاوا
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 برای ضبط چندین جریان صوتی و تصویری به طور همزمان استفاده کنید. در نسخههای قبلی اندروید میتوانید هر بار فقط یک آهنگ صوتی و/یا یک آهنگ ویدیویی ضبط کنید.
از متد 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 برای پخش صدا استفاده می کند.
کاتلین
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; } } }
بیشتر بدانید
این صفحات موضوعات مربوط به ضبط، ذخیره و پخش صدا و تصویر را پوشش می دهند.