نمای کلی MediaRecorder

چارچوب چندرسانه‌ای 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;
        }
    }
}

بیشتر بدانید

این صفحه‌ها موضوعات مربوط به ضبط، ذخیره، و بازپخش صدا و ویدیو را پوشش می‌دهند.