يتضمن إطار عمل الوسائط المتعددة في Android دعمًا لالتقاط وتشفير مجموعة متنوعة من أنواع
الملفات الصوتية وتنسيقات الفيديو. يمكنك استخدام واجهات برمجة تطبيقات MediaRecorder
في حال توفّرها.
بواسطة مكونات الجهاز.
يوضح هذا المستند كيفية استخدام MediaRecorder
لكتابة تطبيق يلتقط الصوت من أحد الأجهزة
الميكروفون وحفظ الصوت وإعادة تشغيله (باستخدام MediaPlayer
). لتسجيل فيديو، عليك تنفيذ ما يلي:
يمكنك استخدام كاميرا الجهاز مع "MediaRecorder
". يتم وصف ذلك في دليل الكاميرا.
ملاحظة: يتعذّر على محاكي Android تسجيل المحتوى الصوت. احرص على اختبار الرمز باستخدام جهاز حقيقي يمكنه التسجيل.
جارٍ طلب إذن لتسجيل الصوت
لكي تتمكّن من التسجيل، يجب أن يخبر التطبيق المستخدم بأنّه سيصل إلى إدخال صوت الجهاز. يجب تضمين علامة الإذن هذه في ملف بيان التطبيق:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
يُعتبَر RECORD_AUDIO
"خطير" الإذن
لأنها قد تشكل خطرًا على خصوصية المستخدم. بدءًا من الإصدار Android 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 (المستوى 26 من واجهة برمجة التطبيقات)، يتوافقMediaRecorder
مع MPEG2_TS وهو مفيد للبث:Kotlin
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)
Java
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS);
- ضبط اسم ملف الإخراج باستخدام
setOutputFile()
يجب تحديد واصف ملف يمثل ملفًا فعليًا. - ضبط برنامج ترميز الصوت باستخدام
setAudioEncoder()
- أكمِل عملية الإعداد من خلال طلب
prepare()
يمكنك بدء المسجّلة الذكية وإيقافها من خلال إجراء مكالمة
start()
و
stop()
على التوالي.
عند الانتهاء من المثيل MediaRecorder
، يجب تفريغ موارده.
في أقرب وقت ممكن من خلال الاتصال
release()
ملاحظة: على الأجهزة التي تعمل بنظام التشغيل Android 9 (المستوى 28 من واجهة برمجة التطبيقات) أو
أعلى، فلن تتمكن التطبيقات التي تعمل في الخلفية من الوصول إلى الميكروفون. ولذلك،
يجب أن يسجِّل تطبيقك الصوت فقط عند تشغيله في المقدّمة أو عند
تضمين مثال لـ MediaRecorder
في
الخدمة التي تعمل في المقدّمة.
استخدام MediaMuxer لتسجيل قنوات متعددة
بدءًا من Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات)، يمكنك استخدام MediaMuxer
لتسجيل عدة عمليات بث صوت وفيديو متزامنة. في الإصدارات السابقة من Android، يمكنك فقط
تسجيل مقطع صوتي واحد و/أو مسار فيديو واحد في كل مرة.
استخدام addTrack()
لمزج مسارات متعددة معًا.
يمكنك أيضًا إضافة مسار بيانات وصفية واحد أو أكثر بمعلومات مخصصة لكل إطار، ولكن فقط لحاويات MP4. يحدّد تطبيقك تنسيق البيانات الوصفية ومحتواها.
إضافة البيانات الوصفية
يمكن أن تكون البيانات الوصفية مفيدة للمعالجة بلا اتصال بالإنترنت. على سبيل المثال، البيانات التي تم التقاطها من يمكن استخدام مستشعر الجيروسكوب لتثبيت الفيديو.
عند إضافة مسار بيانات وصفية، يجب أن يبدأ تنسيق MIME للمقطع بالبادئة.
application/
كتابة بيانات التعريف هي نفسها كتابة بيانات الفيديو أو الصوت، باستثناء
أن البيانات لا تأتي من MediaCodec
. بدلاً من ذلك، يجتاز التطبيق
ByteBuffer
مرتبط بطابع زمني
طريقة writeSampleData()
.
يجب أن يكون الطابع الزمني في القاعدة الزمنية نفسها التي يتضمّنها الفيديو والمقاطع الصوتية.
يستخدم ملف MP4 الذي تم إنشاؤه ملف TextMetaDataSampleEntry
المحدّد في الفقرة 12.3.3.2.
لمواصفات ISO BMFF
للإشارة إلى تنسيق MIME للبيانات الوصفية. عند استخدام MediaExtractor
لاستخراج ملف يحتوي على مقاطع صوتية للبيانات الوصفية، يتم إنشاء نسخة MIME الخاصة بالبيانات الوصفية
كمثيل لـ MediaFormat
.
نموذج التعليمات البرمجية
MediaRecorder نموذج يوضح كيفية إجراء تسجيل فيديو باستخدام MediaRecorder وواجهة برمجة تطبيقات الكاميرا.
يوضّح مثال النشاط أدناه كيفية استخدام 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 } }
Java
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; } } }
مزيد من المعلومات
تتناول هذه الصفحات المواضيع المتعلقة بتسجيل الصوت والفيديو وتخزينهما وتشغيلهما.