Android মাল্টিমিডিয়া ফ্রেমওয়ার্কে বিভিন্ন ধরনের সাধারণ অডিও ও ভিডিও ফর্ম্যাট ক্যাপচার ও এনকোড করার সুবিধা অন্তর্ভুক্ত আছে। ডিভাইস হার্ডওয়্যার সাপোর্ট করলে, আপনি MediaRecorder API
ব্যবহার করতে পারবেন।
এই ডকুমেন্ট থেকে আপনি জানতে পারবেন যে কীভাবে MediaRecorder ব্যবহার করে এমন একটি অ্যাপ্লিকেশন লিখতে হয় যা ডিভাইসের মাইক্রোফোন থেকে অডিও ক্যাপচার করে, অডিও সেভ করে এবং সেটি প্লেব্যাক করে (MediaPlayer-এর সাথে)। ভিডিও রেকর্ড করার জন্য আপনাকে MediaRecorder-এর সাথে ডিভাইসের ক্যামেরা ব্যবহার করতে হবে। এটি ক্যামেরা গাইডে বর্ণনা করা হয়েছে।
মনে রাখবেন: Android Emulator অডিও রেকর্ড করতে পারে না। রেকর্ড করতে পারে এমন একটি আসল ডিভাইসে আপনার কোড পরীক্ষা করতে ভুলবেন না।
অডিও রেকর্ড করার অনুমতি চাওয়া হচ্ছে
রেকর্ড করার জন্য, আপনার অ্যাপকে অবশ্যই ব্যবহারকারীকে জানাতে হবে যে এটি ডিভাইসের অডিও ইনপুট অ্যাক্সেস করবে। অ্যাপের ম্যানিফেস্ট ফাইলে আপনাকে এই অনুমতি ট্যাগ অন্তর্ভুক্ত করতে হবে:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
RECORD_AUDIO-কে
"বিপজ্জনক" অনুমতি হিসেবে বিবেচনা করা হয়
কারণ এটি ব্যবহারকারীর গোপনীয়তার ক্ষেত্রে ঝুঁকি তৈরি করতে পারে। Android 6.0 (API লেভেল ২৩) থেকে শুরু করে, বিপজ্জনক অনুমতি ব্যবহার করে এমন অ্যাপকে
রান টাইমে ব্যবহারকারীর থেকে অনুমোদন চাইতে হবে। ব্যবহারকারী অনুমতি দেওয়ার পরে, অ্যাপের সেটি মনে রাখা উচিত এবং আবার জিজ্ঞাসা করা উচিত নয়। নিচে দেওয়া স্যাম্পেল কোড থেকে জানতে পারবেন যে
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 লেভেল ২৬) থেকে শুরু করেMediaRecorderMPEG2_TS ফর্ম্যাট সাপোর্ট করে, যা স্ট্রিমিংয়ের জন্য উপযোগী:Kotlin
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
জাভা
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
setOutputFile()ব্যবহার করে আউটপুট ফাইলের নাম সেট করুন। আপনাকে অবশ্যই একটি ফাইল ডেসক্রিপটর উল্লেখ করতে হবে যা একটি আসল ফাইলকে উপস্থাপন করে।setAudioEncoder()ব্যবহার করে অডিও এনকোডার সেট করুন।prepare()-এ কল করে ইনিশিয়ালাইজেশন সম্পূর্ণ করুন।
যথাক্রমে
start() এবং
stop()-এ
কল করে রেকর্ডার চালু ও বন্ধ করুন।
MediaRecorder ইনস্ট্যান্সের কাজ হয়ে গেলে,
release() কল করে যত তাড়াতাড়ি সম্ভব
এর রিসোর্স খালি করুন।
মনে রাখবেন: Android 9 (API লেভেল ২৮) বা তার পরবর্তী ভার্সন
চালানো ডিভাইসে, ব্যাকগ্রাউন্ডে চলা অ্যাপ মাইক্রোফোন অ্যাক্সেস করতে পারে না। তাই,
আপনার অ্যাপ শুধুমাত্র ফোরগ্রাউন্ডে থাকাকালীন বা আপনি
ফোরগ্রাউন্ড সার্ভিসে MediaRecorder-এর ইনস্ট্যান্স
যোগ করলে তবেই অডিও রেকর্ড করা উচিত।
একাধিক চ্যানেল রেকর্ড করতে MediaMuxer ব্যবহার করা
Android 8.0 (API লেভেল ২৬) থেকে শুরু করে, আপনি MediaMuxer
ব্যবহার করে একসাথে একাধিক অডিও ও ভিডিও স্ট্রিম রেকর্ড করতে পারবেন। Android-এর আগের ভার্সনে আপনি একবারে শুধুমাত্র
একটি অডিও ট্র্যাক এবং/অথবা একটি ভিডিও ট্র্যাক রেকর্ড করতে পারবেন।
একাধিক ট্র্যাক একসাথে মিক্স করতে addTrack()
পদ্ধতি ব্যবহার করুন।
এছাড়াও, আপনি প্রতিটি ফ্রেমের জন্য কাস্টম তথ্য সহ এক বা একাধিক মেটাডেটা ট্র্যাক যোগ করতে পারেন, তবে শুধুমাত্র MP4 কন্টেনারে। আপনার অ্যাপ মেটাডেটার ফর্ম্যাট ও কন্টেন্ট নির্ধারণ করে।
মেটাডেটা যোগ করা হচ্ছে
অফলাইন প্রসেসিংয়ের জন্য মেটাডেটা উপযোগী হতে পারে। যেমন, ভিডিও স্টেবিলাইজেশনের জন্য জাইরো সেন্সর থেকে ক্যাপচার করা ডেটা ব্যবহার করা হতে পারে।
আপনি কোনও মেটাডেটা ট্র্যাক যোগ করলে, ট্র্যাকের মাইম ফর্ম্যাট অবশ্যই প্রিফিক্স
application/ দিয়ে শুরু হতে হবে। মেটাডেটা লেখা, ভিডিও বা অডিও ডেটা লেখার মতোই, তবে
এই ডেটা MediaCodec থেকে আসে না। পরিবর্তে, অ্যাপটি
writeSampleData() পদ্ধতিতে সংশ্লিষ্ট টাইমস্ট্যাম্প সহ একটি
ByteBuffer পাস করে।
ভিডিও ও অডিও ট্র্যাকের মতো টাইমস্ট্যাম্পও একই টাইম বেসে থাকতে হবে।
জেনারেট করা MP4 ফাইলটি ISO BMFF স্পেসিফিকেশনের
১২.৩.৩.২ বিভাগে সংজ্ঞায়িত TextMetaDataSampleEntry ব্যবহার করে
মেটাডেটার মাইম ফর্ম্যাট সিগন্যাল করে। মেটাডেটা ট্র্যাক আছে এমন ফাইল এক্সট্র্যাক্ট করতে MediaExtractor ব্যবহার করলে, মেটাডেটার mime
ফর্ম্যাট MediaFormat-এর ইনস্ট্যান্স হিসেবে দেখা যায়।
নমুনা কোড
MediaRecorder স্যাম্পেল দেখায় যে কীভাবে MediaRecorder এবং Camera API ব্যবহার করে ভিডিও রেকর্ডিং করতে হয়।
নিচের অ্যাক্টিভিটির উদাহরণ থেকে বোঝা যাবে যে কীভাবে MediaRecorder ব্যবহার করে অডিও ফাইল রেকর্ড করতে হয়। এছাড়াও,
অডিও প্লেব্যাক করার জন্য এটি MediaPlayer ব্যবহার করে।
Kotlin
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; } } }
আরও জানুন
এইসব পৃষ্ঠায় অডিও ও ভিডিও রেকর্ড, স্টোর ও প্লেব্যাক করা সম্পর্কিত বিষয় কভার করা হয়।