چارچوب چند رسانه ای اندروید شامل پشتیبانی از ضبط و کدگذاری انواع فرمت های رایج صوتی و تصویری است. اگر توسط سخت افزار دستگاه پشتیبانی می شود، می توانید از API های MediaRecorder استفاده کنید.
این سند به شما نشان می دهد که چگونه از MediaRecorder برای نوشتن برنامه ای استفاده کنید که صدا را از میکروفون دستگاه ضبط می کند، صدا را ذخیره می کند و آن را پخش می کند (با MediaPlayer ). برای ضبط ویدیو باید از دوربین دستگاه به همراه MediaRecorder استفاده کنید. این در راهنمای دوربین توضیح داده شده است.
توجه: شبیه ساز اندروید نمی تواند صدا را ضبط کند. حتما کد خود را روی یک دستگاه واقعی که قابلیت ضبط را دارد تست کنید.
برای اینکه بتوانید ضبط کنید، برنامه شما باید به کاربر بگوید که به ورودی صوتی دستگاه دسترسی خواهد داشت. باید این تگ مجوز را در فایل مانیفست برنامه قرار دهید:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
RECORD_AUDIO یک مجوز "خطرناک" در نظر گرفته می شود زیرا ممکن است برای حریم خصوصی کاربر خطری ایجاد کند. با شروع Android 6.0 (سطح API 23)، برنامهای که از مجوز خطرناکی استفاده میکند باید در زمان اجرا از کاربر درخواست تأیید کند. پس از اعطای مجوز به کاربر، برنامه باید به خاطر بسپارد و دیگر درخواست نکند. کد نمونه زیر نحوه پیاده سازی این رفتار را با استفاده از ActivityCompat.requestPermissions() نشان می دهد.
نمونه جدیدی از 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) یا بالاتر، برنامههایی که در پسزمینه اجرا میشوند نمیتوانند به میکروفون دسترسی داشته باشند. بنابراین، برنامه شما باید صدا را فقط زمانی ضبط کند که در پیشزمینه باشد یا زمانی که نمونهای از با شروع Android 8.0 (سطح API 26) می توانید از از متد همچنین می توانید یک یا چند تراک فراداده را با اطلاعات سفارشی برای هر فریم، اما فقط به کانتینرهای MP4 اضافه کنید. برنامه شما قالب و محتوای فراداده را تعریف می کند. ابرداده می تواند برای پردازش آفلاین مفید باشد. به عنوان مثال، داده های گرفته شده از سنسور ژیروسکوپ می تواند برای انجام تثبیت ویدئو استفاده شود. هنگامی که یک آهنگ فراداده را اضافه می کنید، فرمت mime آهنگ باید با فایل MP4 تولید شده از نمونه MediaRecorder نشان می دهد که چگونه با استفاده از MediaRecorder و Camera API یک فیلم ضبط کنید. فعالیت مثال زیر نحوه استفاده از این صفحات موضوعات مربوط به ضبط، ذخیره و پخش صدا و تصویر را پوشش می دهند.MediaRecorder را در یک سرویس پیشزمینه قرار میدهید.MediaMuxer برای ضبط چندین جریان صوتی و تصویری به طور همزمان استفاده کنید. در نسخههای قبلی اندروید میتوانید هر بار فقط یک آهنگ صوتی و/یا یک آهنگ ویدیویی ضبط کنید.addTrack() برای ترکیب چند آهنگ با هم استفاده کنید.application/ شروع شود. نوشتن ابرداده مانند نوشتن داده های ویدیویی یا صوتی است، با این تفاوت که داده ها از MediaCodec نمی آیند. در عوض، برنامه یک ByteBuffer با مهر زمانی مرتبط به متد writeSampleData() ارسال می کند. مُهر زمانی باید در پایه زمانی تراکهای ویدیویی و صوتی باشد.TextMetaDataSampleEntry تعریف شده در بخش 12.3.3.2 مشخصات ISO BMFF برای سیگنال دادن به فرمت mime فراداده استفاده می کند. وقتی از MediaExtractor برای استخراج فایلی استفاده میکنید که حاوی آهنگهای فراداده است، فرمت mime فراداده به عنوان نمونه MediaFormat ظاهر میشود.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;
}
}
}