چارچوب چند رسانه ای اندروید شامل پشتیبانی از ضبط و کدگذاری انواع فرمت های رایج صوتی و تصویری است. اگر توسط سخت افزار دستگاه پشتیبانی می شود، می توانید از 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; } } }
بیشتر بدانید
این صفحات موضوعات مربوط به ضبط، ذخیره و پخش صدا و تصویر را پوشش می دهند.