মিডিয়া রেকর্ডার ওভারভিউ

অ্যান্ড্রয়েড মাল্টিমিডিয়া ফ্রেমওয়ার্ক বিভিন্ন সাধারণ অডিও এবং ভিডিও ফরম্যাট ক্যাপচার এবং এনকোডিংয়ের জন্য সমর্থন অন্তর্ভুক্ত করে। ডিভাইস হার্ডওয়্যার দ্বারা সমর্থিত হলে আপনি MediaRecorder API ব্যবহার করতে পারেন।

এই দস্তাবেজটি আপনাকে দেখায় কিভাবে MediaRecorder ব্যবহার করে এমন একটি অ্যাপ্লিকেশন লিখতে হয় যা একটি ডিভাইস মাইক্রোফোন থেকে অডিও ক্যাপচার করে, অডিও সংরক্ষণ করে এবং এটিকে আবার চালায় ( MediaPlayer এর সাথে)। ভিডিও রেকর্ড করার জন্য আপনাকে MediaRecorder এর সাথে ডিভাইসের ক্যামেরা ব্যবহার করতে হবে। এটি ক্যামেরা গাইডে বর্ণনা করা হয়েছে।

দ্রষ্টব্য: অ্যান্ড্রয়েড এমুলেটর অডিও রেকর্ড করতে পারে না। রেকর্ড করতে পারে এমন একটি বাস্তব ডিভাইসে আপনার কোড পরীক্ষা করতে ভুলবেন না।

অডিও রেকর্ড করার অনুমতির অনুরোধ করা হচ্ছে

রেকর্ড করতে সক্ষম হওয়ার জন্য, আপনার অ্যাপটিকে অবশ্যই ব্যবহারকারীকে বলতে হবে যে এটি ডিভাইসের অডিও ইনপুট অ্যাক্সেস করবে। আপনাকে অবশ্যই অ্যাপের ম্যানিফেস্ট ফাইলে এই অনুমতি ট্যাগটি অন্তর্ভুক্ত করতে হবে:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

RECORD_AUDIO একটি "বিপজ্জনক" অনুমতি হিসাবে বিবেচনা করা হয় কারণ এটি ব্যবহারকারীর গোপনীয়তার জন্য ঝুঁকি তৈরি করতে পারে৷ অ্যান্ড্রয়েড 6.0 (এপিআই লেভেল 23) দিয়ে শুরু করে একটি বিপজ্জনক অনুমতি ব্যবহার করে এমন একটি অ্যাপ ব্যবহার করার সময় ব্যবহারকারীকে অনুমোদনের জন্য জিজ্ঞাসা করতে হবে। ব্যবহারকারীর অনুমতি দেওয়ার পরে, অ্যাপটিকে মনে রাখা উচিত এবং আবার জিজ্ঞাসা করা উচিত নয়। নিচের নমুনা কোডটি ActivityCompat.requestPermissions() ব্যবহার করে এই আচরণ কিভাবে বাস্তবায়ন করতে হয় তা দেখায়।

একটি MediaRecorder তৈরি এবং চালানো

নিম্নলিখিত কলগুলির সাথে MediaRecorder এর একটি নতুন উদাহরণ শুরু করুন:

  • setAudioSource() ব্যবহার করে অডিও উৎস সেট করুন। আপনি সম্ভবত MIC ব্যবহার করবেন।

    দ্রষ্টব্য: বেশিরভাগ অডিও উত্স ( DEFAULT সহ) অডিও সিগন্যালে প্রক্রিয়াকরণ প্রয়োগ করে৷ কাঁচা অডিও রেকর্ড করতে UNPROCESSED নির্বাচন করুন। কিছু ডিভাইস অপ্রক্রিয়াজাত ইনপুট সমর্থন করে না। এটি উপলব্ধ আছে কিনা তা যাচাই করতে প্রথমে AudioManager.getProperty(AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED) এ কল করুন৷ যদি তা না হয়, তাহলে পরিবর্তে VOICE_RECOGNITION ব্যবহার করার চেষ্টা করুন, যা AGC বা শব্দ দমন করে না। সম্পত্তিটি সমর্থিত না থাকলেও আপনি একটি অডিও উত্স হিসাবে UNPROCESSED ব্যবহার করতে পারেন, তবে সেই ক্ষেত্রে সংকেতটি অপ্রসেসড হবে কিনা তার কোনও গ্যারান্টি নেই৷

  • setOutputFormat() ব্যবহার করে আউটপুট ফাইল বিন্যাস সেট করুন। মনে রাখবেন যে Android 8.0 (API লেভেল 26) দিয়ে শুরু করে 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 9 (API স্তর 28) বা উচ্চতর চলমান ডিভাইসগুলিতে, পটভূমিতে চলমান অ্যাপগুলি মাইক্রোফোন অ্যাক্সেস করতে পারে না। অতএব, আপনার অ্যাপটি শুধুমাত্র তখনই অডিও রেকর্ড করবে যখন এটি ফোরগ্রাউন্ডে থাকে বা যখন আপনি একটি ফোরগ্রাউন্ড পরিষেবাতে MediaRecorder এর একটি উদাহরণ অন্তর্ভুক্ত করেন।

একাধিক চ্যানেল রেকর্ড করতে MediaMuxer ব্যবহার করে

Android 8.0 (API লেভেল 26) দিয়ে শুরু করে আপনি একাধিক অডিও এবং ভিডিও স্ট্রীম রেকর্ড করতে একটি MediaMuxer ব্যবহার করতে পারেন। অ্যান্ড্রয়েডের আগের সংস্করণগুলিতে আপনি একবারে শুধুমাত্র একটি অডিও ট্র্যাক এবং/অথবা একটি ভিডিও ট্র্যাক রেকর্ড করতে পারেন৷

একাধিক ট্র্যাক একসাথে মিশ্রিত করতে addTrack() পদ্ধতি ব্যবহার করুন।

আপনি প্রতিটি ফ্রেমের জন্য কাস্টম তথ্য সহ এক বা একাধিক মেটাডেটা ট্র্যাক যোগ করতে পারেন, তবে শুধুমাত্র MP4 পাত্রে। আপনার অ্যাপটি মেটাডেটার বিন্যাস এবং বিষয়বস্তু নির্ধারণ করে।

মেটাডেটা যোগ করা হচ্ছে

মেটাডেটা অফলাইন প্রক্রিয়াকরণের জন্য উপযোগী হতে পারে। উদাহরণস্বরূপ, গাইরো সেন্সর থেকে ক্যাপচার করা ডেটা ভিডিও স্ট্যাবিলাইজেশন করতে ব্যবহার করা যেতে পারে।

যখন আপনি একটি মেটাডেটা ট্র্যাক যোগ করেন, তখন ট্র্যাকের মাইম ফর্ম্যাটটি অবশ্যই উপসর্গ application/ দিয়ে শুরু হতে হবে। মেটাডেটা লেখা ভিডিও বা অডিও ডেটা লেখার মতোই, ডেটা MediaCodec থেকে আসে না। পরিবর্তে, অ্যাপটি writeSampleData() পদ্ধতিতে সংশ্লিষ্ট টাইমস্ট্যাম্প সহ একটি ByteBuffer পাস করে। টাইমস্ট্যাম্প অবশ্যই ভিডিও এবং অডিও ট্র্যাকের মতো একই টাইম বেস হতে হবে।

জেনারেট করা MP4 ফাইলটি মেটাডেটার মাইম ফরম্যাটকে সংকেত দিতে ISO BMFF স্পেসিফিকেশনের 12.3.3.2 বিভাগে সংজ্ঞায়িত TextMetaDataSampleEntry ব্যবহার করে। আপনি যখন মেটাডেটা ট্র্যাক ধারণ করে এমন একটি ফাইল বের করতে একটি MediaExtractor ব্যবহার করেন, তখন মেটাডেটার মাইম ফরম্যাটটি 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;
        }
    }
}

আরও জানুন

এই পৃষ্ঠাগুলি অডিও এবং ভিডিও রেকর্ডিং, সঞ্চয় এবং প্লে ব্যাক সম্পর্কিত বিষয়গুলি কভার করে৷