يتضمن إطار عمل الوسائط المتعددة في Android إمكانية تشغيل مجموعة متنوعة من أنواع الوسائط الشائعة بحيث يمكنك دمج الصوت والفيديو والصور في تطبيقاتك بسهولة. يمكنك تشغيل صوت أو فيديو من ملفات الوسائط المخزَّنة في موارد تطبيقك (الموارد الأولية)، أو من ملفات مستقلّة
في نظام الملفات، أو من مصدر بيانات يصل عبر اتصال شبكة، وكل ذلك باستخدام واجهات برمجة تطبيقات MediaPlayer
.
يوضِّح لك هذا المستند كيفية استخدام
MediaPlayer
لكتابة تطبيق تشغيل وسائط
يتفاعل مع المستخدم والنظام من أجل الحصول على أداء جيد
وتجربة ممتعة للمستخدم. وبدلاً من ذلك، يمكنك استخدام ExoPlayer، وهي مكتبة مفتوحة المصدر قابلة للتخصيص وتتوافق مع ميزات عالية الأداء غير متاحة في MediaPlayer
.
ملاحظة: يمكنك تشغيل البيانات الصوتية على جهاز الإخراج العادي فقط. وفي الوقت الحالي، يمكن استخدام مكبّر الصوت الخاص بالجهاز الجوّال أو سماعة الرأس التي تعمل بالبلوتوث. لا يمكنك تشغيل ملفات صوتية في صوت المحادثة أثناء المكالمة.
الأساسيّات
تُستخدَم الفئات التالية لتشغيل الصوت والفيديو في إطار عمل Android:
MediaPlayer
- هذه الفئة هي واجهة برمجة التطبيقات الأساسية لتشغيل الصوت والفيديو.
AudioManager
- يدير هذا الصف مصادر الصوت وإخراج الصوت على الجهاز.
بيانات البيان
قبل بدء تطوير تطبيقك باستخدام MediaPlayer، يُرجى التأكّد من أنّ ملف البيان يتضمّن التعريفات المناسبة التي تتيح استخدام الميزات ذات الصلة.
- إذن الإنترنت - إذا كنت تستخدم MediaPlayer لبث محتوى مستند إلى الشبكة، يجب أن يطلب التطبيق الوصول إلى الشبكة.
<uses-permission android:name="android.permission.INTERNET" />
- إذن قفل التنشيط - إذا كان تطبيق المشغّل بحاجة إلى منع تعتيم الشاشة أو منع المعالج من الدخول في وضع السكون أو إذا كان يستخدم طريقة
MediaPlayer.setScreenOnWhilePlaying()
أوMediaPlayer.setWakeMode()
، يجب طلب هذا الإذن.<uses-permission android:name="android.permission.WAKE_LOCK" />
استخدام MediaPlayer
تُعدّ الفئة MediaPlayer
من أهم مكوّنات إطار العمل الإعلامي. يمكن لعنصر من هذه الفئة جلب الصوت والفيديو وفك ترميزه وتشغيلهما
بأقل قدر من الإعداد. وهو يتوافق مع العديد من مصادر الوسائط المختلفة مثل:
- المراجع المحلية
- معرفات الموارد المنتظمة (URI) الداخلية، مثل معرف موارد منتظم (URI) قد تحصل عليه من برنامج تعيين المحتوى
- عناوين URL الخارجية (بث)
للحصول على قائمة بتنسيقات الوسائط التي يدعمها Android، راجع صفحة تنسيقات الوسائط المتوافقة.
في ما يلي مثال
على كيفية تشغيل ملف صوتي متوفّر كمورد محلّي أولي (محفوظ في دليل
res/raw/
في تطبيقك):
Kotlin
var mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1) mediaPlayer.start() // no need to call prepare(); create() does that for you
Java
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1); mediaPlayer.start(); // no need to call prepare(); create() does that for you
في هذه الحالة، المورد "الأول" هو ملف لا يحاول النظام تحليله بأي طريقة معينة. مع ذلك، يجب ألا يكون محتوى هذا المورد صوتًا أوّليًا. ويجب أن يكون ملف وسائط مرمّزًا ومنسقًا بشكل صحيح بأحد التنسيقات المتوافقة.
وإليك كيفية التشغيل من عنوان URI متوفر محليًا في النظام (الذي حصلت عليه من خلال محلل المحتوى، على سبيل المثال):
Kotlin
val myUri: Uri = .... // initialize Uri here val mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(applicationContext, myUri) prepare() start() }
Java
Uri myUri = ....; // initialize Uri here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioAttributes( new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(getApplicationContext(), myUri); mediaPlayer.prepare(); mediaPlayer.start();
يبدو التشغيل من عنوان URL بعيد عبر بث HTTP على النحو التالي:
Kotlin
val url = "http://........" // your URL here val mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(url) prepare() // might take long! (for buffering, etc) start() }
Java
String url = "http://........"; // your URL here MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioAttributes( new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); // might take long! (for buffering, etc) mediaPlayer.start();
ملاحظة: إذا كنت تمرِّر عنوان URL لبث ملف وسائط على الإنترنت، يجب أن يكون الملف قابلاً للتنزيل التدريجي.
تنبيه: يجب التقاط
IllegalArgumentException
وIOException
أو تمريرهما عند استخدام
setDataSource()
لأنّ الملف الذي تشير إليه قد لا يكون متوفرًا.
تحضير غير متزامن
يمكن أن يكون استخدام MediaPlayer
بسيطًا من حيث المبدأ. ومع ذلك، من المهم أن تضع في اعتبارك أن هناك بعض الأمور الأخرى
الضرورية لدمجها بشكل صحيح مع تطبيق Android نموذجي. على سبيل المثال، قد يستغرق تنفيذ طلب prepare()
وقتًا طويلاً، لأنّه قد يتضمّن جلب بيانات الوسائط وفك ترميزها. وبالتالي، كما هو الحال مع أي طريقة قد تستغرق وقتًا طويلاً في التنفيذ، يجب عدم استدعاءها أبدًا من
سلسلة محادثات واجهة المستخدم في التطبيق. يؤدي ذلك إلى تعليق واجهة المستخدم إلى أن تظهر الطريقة، وهو ما يترك انطباعًا سيئًا لدى المستخدم ويمكن أن يتسبب في حدوث خطأ ANR (التطبيق لا يستجيب). حتى لو كنت تتوقع أن يتم تحميل المورد بسرعة، فتذكر أن أي شيء يستغرق أكثر من عشر
ثانية للاستجابة في واجهة المستخدم يتسبب في توقف مؤقت ملحوظ ويعطي المستخدم انطباعًا بأن تطبيقك بطيء.
لتجنُّب تعليق سلسلة التعليمات في واجهة المستخدم، أنشِئ سلسلة محادثات أخرى
لإعداد MediaPlayer
وإشعار سلسلة التعليمات الرئيسية عند الانتهاء. ومع ذلك،
يمكنك كتابة منطق سلاسل المحادثات
بنفسك، فإن هذا النمط شائع جدًا عند استخدام MediaPlayer
لدرجة أنّ إطار العمل
يوفّر طريقة ملائمة لإنجاز هذه المهمة باستخدام
طريقة prepareAsync()
. تبدأ هذه الطريقة في إعداد الوسائط في الخلفية
وتعود على الفور. عند الانتهاء من تحضير الوسائط، يتم استدعاء طريقة onPrepared()
من MediaPlayer.OnPreparedListener
التي تم ضبطها من خلال setOnPreparedListener()
.
حالة الإدارة
يجب أن تضع في اعتبارك جانبًا آخر من جوانب MediaPlayer
،
وهو أنّه يعتمد على الدولة. وهذا يعني أنّ MediaPlayer
له حالة داخلية يجب أن تكون على دراية بها دائمًا عند كتابة الرموز البرمجية، لأنّ عمليات معيّنة تكون صالحة فقط عندما يكون المشغّل في حالات معيّنة. وإذا أجريت عملية في حالة خاطئة، قد يعرض النظام استثناءً أو يتسبب في سلوكيات أخرى غير مرغوب فيها.
وتعرض المستندات في الفئة MediaPlayer
رسمًا بيانيًا كاملاً للحالة يوضّح الطرق التي تنقل MediaPlayer
من حالة إلى أخرى.
على سبيل المثال، عند إنشاء MediaPlayer
جديدة، تكون في حالة عدم النشاط. في هذه المرحلة، يجب إعداده عن طريق استدعاء
setDataSource()
، وعرض
الحالة تم الإعداد. بعد ذلك، عليك تحضيره باستخدام طريقة
prepare()
أو
prepareAsync()
. عند
الانتهاء من تحضير ميزة MediaPlayer
، تدخل الحالة Prepared،
ما يعني أنّه يمكنك طلب start()
لتشغيلها على الوسائط. وكما يوضِّح الرسم التوضيحي،
يمكنك التنقّل بين الحالة تم بدء التشغيل وتم الإيقاف مؤقتًا وتم إكمال التشغيل من خلال
استدعاء طرق مثل
start()
وpause()
و
seekTo()
،
من بين طرق أخرى. ومع ذلك، عند
الاتصال بـ stop()
، لاحظ أنه
لا يمكنك الاتصال بـ start()
مرة أخرى حتى
تجهز MediaPlayer
مرة أخرى.
يجب دائمًا مراعاة مخطط الحالة
عند كتابة تعليمات برمجية تتفاعل مع
كائن MediaPlayer
، لأنّ استدعاء طُرقه من الحالة الخاطئة هو
سبب شائع للأخطاء.
إلغاء تشغيل MediaPlayer
ويمكن أن تستهلك السمة MediaPlayer
موارد نظام قيّمة.
لذلك، يجب دائمًا اتخاذ احتياطات إضافية للتأكد من عدم
التمسك بمثيل MediaPlayer
لفترة أطول من اللازم. عند
الانتهاء من ذلك، يجب دائمًا استدعاء
release()
للتأكد من إلغاء أي
موارد نظام مخصصة له بشكل صحيح. على سبيل المثال، إذا كنت
تستخدم MediaPlayer
وتلقى نشاطك مكالمة إلى onStop()
، يجب رفع MediaPlayer
،
لأنه من غير المنطقي الاحتفاظ به أثناء عدم تفاعل نشاطك مع المستخدم (إلا إذا كنت تشغّل الوسائط في الخلفية، وهو ما تمت مناقشته في القسم التالي).
عند استئناف النشاط أو إعادة تشغيله، يجب بالطبع إنشاء MediaPlayer
جديد وإعداده من جديد قبل استئناف التشغيل.
في ما يلي كيفية سحب MediaPlayer
ثم إبطالها:
Kotlin
mediaPlayer?.release() mediaPlayer = null
Java
mediaPlayer.release(); mediaPlayer = null;
على سبيل المثال، ضع في اعتبارك المشاكل التي قد تحدث إذا نسيت إطلاق MediaPlayer
عند إيقاف نشاطك، وأنشئ نشاطًا جديدًا عند بدء النشاط من جديد. كما تعلم، عندما يغيّر المستخدم اتجاه الشاشة (أو يغيّر إعدادات الجهاز بطريقة أخرى)،
يتعامل النظام مع ذلك من خلال إعادة تشغيل النشاط (بشكل تلقائي)، لذلك يمكنك استهلاك جميع موارد النظام بسرعة بينما يحرّك المستخدم الجهاز ذهابًا وإيابًا بين الوضعين العمودي والأفقي، لأنه عند كل
تغيير في الاتجاه، يمكنك إنشاء MediaPlayer
جديدة لا ترفعها مطلقًا. (لمزيد من المعلومات حول عمليات إعادة تشغيل وقت التشغيل، يُرجى الاطّلاع على التعامل مع تغييرات وقت التشغيل.)
قد تتساءل عمّا سيحدث إذا أردت مواصلة تشغيل "الوسائط في الخلفية" حتى عندما يغادر المستخدم نشاطك، تمامًا كما يتصرف تطبيق "موسيقى Google Play" المُدمَج. في هذه الحالة، ما تحتاج إليه هو "MediaPlayer
" يتم التحكّم فيه من خلال إحدى الخدمات، كما
تمت مناقشته في القسم التالي.
استخدام MediaPlayer في إحدى الخدمات
إذا كنت تريد تشغيل الوسائط في الخلفية حتى في حال عدم ظهور تطبيقك على الشاشة، أي رغبتك في مواصلة تشغيلها أثناء تفاعل المستخدم مع التطبيقات الأخرى، عليك بدء إحدى الخدمات والتحكم في مثيل MediaPlayer
من هناك.
يجب تضمين
MediaPlayer في خدمة MediaBrowserServiceCompat
وطلب
التفاعل مع
MediaBrowserCompat
في نشاط آخر.
يجب توخي الحذر بشأن إعداد العميل/الخادم هذا. هناك توقعات حول كيفية تفاعل اللاعب الذي يعمل في خدمة في الخلفية مع بقية النظام. إذا لم يلبّي تطبيقك تلك التوقعات، فقد تكون تجربة المستخدم سيئة. اقرأ إنشاء تطبيق صوتي للحصول على التفاصيل الكاملة.
يصف هذا القسم تعليمات خاصة لإدارة MediaPlayer عند تنفيذها داخل إحدى الخدمات.
التشغيل بشكل غير متزامن
أولاً، مثل Activity
، يتم بشكل تلقائي كل الأعمال في Service
ضمن سلسلة محادثات واحدة. وإذا كنت تشغِّل نشاطًا وخدمة من التطبيق نفسه، يتم استخدام سلسلة التعليمات نفسها ("سلسلة التعليمات الرئيسية") تلقائيًا. لذلك، تحتاج الخدمات إلى معالجة
النوايا الواردة بسرعة وعدم إجراء عمليات حسابية طويلة عند الاستجابة لها. إذا كان من المتوقع إجراء أي استدعاءات بعمل شاق أو حظر، عليك تنفيذ هذه المهام بشكل غير متزامن: إما من
سلسلة محادثات أخرى تنفذها بنفسك، أو من خلال استخدام مرافق إطار العمل العديدة
للمعالجة غير المتزامنة.
على سبيل المثال، عند استخدام MediaPlayer
من سلسلة التعليمات الرئيسية،
يجب طلب prepareAsync()
بدلاً من prepare()
، وتنفيذ
MediaPlayer.OnPreparedListener
ليتم إعلامك عند اكتمال عملية الإعداد وبدء اللعب.
مثلاً:
Kotlin
private const val ACTION_PLAY: String = "com.example.action.PLAY" class MyService: Service(), MediaPlayer.OnPreparedListener { private var mMediaPlayer: MediaPlayer? = null override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { ... val action: String = intent.action when(action) { ACTION_PLAY -> { mMediaPlayer = ... // initialize it here mMediaPlayer?.apply { setOnPreparedListener(this@MyService) prepareAsync() // prepare async to not block main thread } } } ... } /** Called when MediaPlayer is ready */ override fun onPrepared(mediaPlayer: MediaPlayer) { mediaPlayer.start() } }
Java
public class MyService extends Service implements MediaPlayer.OnPreparedListener { private static final String ACTION_PLAY = "com.example.action.PLAY"; MediaPlayer mediaPlayer = null; public int onStartCommand(Intent intent, int flags, int startId) { ... if (intent.getAction().equals(ACTION_PLAY)) { mediaPlayer = ... // initialize it here mediaPlayer.setOnPreparedListener(this); mediaPlayer.prepareAsync(); // prepare async to not block main thread } } /** Called when MediaPlayer is ready */ public void onPrepared(MediaPlayer player) { player.start(); } }
معالجة الأخطاء غير المتزامنة
في العمليات المتزامنة، يتم عادةً الإشارة إلى الأخطاء باستخدام رمز استثناء أو رمز خطأ، ولكن كلما استخدمت موارد غير متزامنة، يجب التأكد من إبلاغ التطبيق بالأخطاء بالشكل المناسب. في حالة MediaPlayer
، يمكنك إنجاز ذلك من خلال تنفيذ MediaPlayer.OnErrorListener
وإعداده في مثيل MediaPlayer
:
Kotlin
class MyService : Service(), MediaPlayer.OnErrorListener { private var mediaPlayer: MediaPlayer? = null fun initMediaPlayer() { // ...initialize the MediaPlayer here... mediaPlayer?.setOnErrorListener(this) } override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean { // ... react appropriately ... // The MediaPlayer has moved to the Error state, must be reset! } }
Java
public class MyService extends Service implements MediaPlayer.OnErrorListener { MediaPlayer mediaPlayer; public void initMediaPlayer() { // ...initialize the MediaPlayer here... mediaPlayer.setOnErrorListener(this); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { // ... react appropriately ... // The MediaPlayer has moved to the Error state, must be reset! } }
من المهم تذكُّر أنّه عند حدوث خطأ، يتم نقل السمة MediaPlayer
إلى الحالة Error (راجِع مستندات الفئة
MediaPlayer
للاطّلاع على الرسم التخطيطي للحالة الكاملة)،
ويجب إعادة ضبطها لتتمكن من استخدامها مرة أخرى.
استخدام ميزة "قفل التنشيط"
عند تصميم التطبيقات التي تشغّل الوسائط في الخلفية، قد ينتقل الجهاز إلى وضع السكون أثناء تشغيل الخدمة. ولأن نظام Android يحاول الحفاظ على طاقة البطارية أثناء وضع الجهاز في وضع السكون، يحاول النظام إيقاف أي من ميزات الهاتف غير الضرورية، بما في ذلك وحدة المعالجة المركزية (CPU) وأجهزة WiFi. ولكن إذا كانت الخدمة قيد التشغيل أو بتشغيل الموسيقى، يجب منع النظام من التدخل في التشغيل.
لضمان استمرار تشغيل خدمتك في ظل هذه الظروف، عليك استخدام "أقفال التنشيط". إن قفل التنشيط هو وسيلة لإرسال إشارة إلى النظام بأن تطبيقك يستخدم بعض الميزات التي من المفترض أن تظل متاحة حتى إذا كان الهاتف في وضع عدم النشاط.
ملاحظة:يجب دائمًا استخدام عمليات قفل التنشيط باعتدال والاحتفاظ بها للفترة اللازمة فقط لأنها تقلِّل بشكل كبير من عمر بطارية الجهاز.
لضمان استمرار عمل وحدة المعالجة المركزية (CPU) أثناء تشغيل MediaPlayer
، يمكنك استدعاء الطريقة setWakeMode()
عند إعداد MediaPlayer
. بعد إجراء ذلك،
يحمل MediaPlayer
مفتاح القفل المحدّد أثناء التشغيل ويرفع القفل عندما يتم إيقافه مؤقتًا أو توقفه:
Kotlin
mediaPlayer = MediaPlayer().apply { // ... other initialization here ... setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK) }
Java
mediaPlayer = new MediaPlayer(); // ... other initialization here ... mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
ومع ذلك، يضمن قفل التنشيط المكتسب في هذا المثال بقاء وحدة المعالجة المركزية (CPU) نشطة فقط. إذا
كنت تبث الوسائط عبر
الشبكة وكنت تستخدم شبكة Wi-Fi، قد يكون من الأفضل الاحتفاظ
برقم WifiLock
أيضًا، الذي يجب الحصول عليه وإفلاته يدويًا. لذلك، عند بدء إعداد MediaPlayer
باستخدام عنوان URL البعيد، عليك إنشاء قفل Wi-Fi والحصول عليه.
مثلاً:
Kotlin
val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager val wifiLock: WifiManager.WifiLock = wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock") wifiLock.acquire()
Java
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); wifiLock.acquire();
عند إيقاف الوسائط مؤقتًا أو إيقافها، أو عند الاستغناء عن الشبكة، يجب إفلات القفل:
Kotlin
wifiLock.release()
Java
wifiLock.release();
جارٍ تنفيذ التنظيف
كما ذكرنا سابقًا، يمكن أن يستهلك الكائن MediaPlayer
قدرًا كبيرًا
من موارد النظام، لذا يجب الاحتفاظ به فقط طيلة الفترة التي تحتاج إليها، ثم طلب
release()
عند الانتهاء من استخدامه. من المهم استدعاء طريقة التنظيف هذه بشكل صريح بدلاً من الاعتماد على جمع البيانات المهملة للنظام لأنّ ذلك قد يستغرق بعض الوقت قبل أن تستردّ أداة تجميع البيانات المهملة MediaPlayer
،
لأنها حساسة فقط لاحتياجات الذاكرة وليس بسبب نقص الموارد الأخرى ذات الصلة بالوسائط.
وبالتالي، في حال استخدامك للخدمة، يجب دائمًا إلغاء طريقة
onDestroy()
لضمان إصدار
MediaPlayer
:
Kotlin
class MyService : Service() { private var mediaPlayer: MediaPlayer? = null // ... override fun onDestroy() { super.onDestroy() mediaPlayer?.release() } }
Java
public class MyService extends Service { MediaPlayer mediaPlayer; // ... @Override public void onDestroy() { super.onDestroy(); if (mediaPlayer != null) mediaPlayer.release(); } }
عليك دائمًا البحث عن فرص أخرى لإصدار MediaPlayer
أيضًا،
باستثناء إصداره بعد إيقافه. على سبيل المثال، إذا كنت تتوقع ألا تتمكن من
تشغيل الوسائط لفترة زمنية طويلة (بعد فقدان تركيز الصوت مثلاً)،
عليك بالتأكيد إصدار MediaPlayer
الحالي وإعادة إنشائه
في وقت لاحق. من ناحية أخرى، إذا كنت تتوقع إيقاف التشغيل لفترة قصيرة جدًا، من الأفضل أن تحتفظ على MediaPlayer
لتجنّب تحمّل تكلفة إنشاء المحتوى وإعداده مرة أخرى.
إدارة الحقوق الرقمية (DRM)
بدءًا من الإصدار Android 8.0 (المستوى 26 من واجهة برمجة التطبيقات)، يتضمّن MediaPlayer
واجهات برمجة تطبيقات
تتيح تشغيل المواد المحمية بموجب إدارة الحقوق الرقمية. هذه الواجهات تتشابه مع واجهة برمجة التطبيقات المنخفضة المستوى التي تقدّمها خدمة
MediaDrm
، ولكنّها تعمل على مستوى أعلى ولا تعرض أدوات استخراج البيانات الأساسية وعناصر drm والعملات المشفّرة.
لا توفّر واجهة برمجة التطبيقات MediaPlayer DRM API الوظائف الكاملة لـ
MediaDrm
، لكنّها متوافقة مع حالات الاستخدام الأكثر شيوعًا. يمكن للتنفيذ الحالي معالجة أنواع المحتوى التالية:
- ملفات الوسائط المحلية المحمية بموجب Waze
- ملفات وسائط البث أو عن بُعد محمية على Waze
يوضح مقتطف الرمز التالي كيفية استخدام طرق DRM MediaPlayer الجديدة بتنفيذ بسيط ومتزامن.
لإدارة الوسائط التي يتم التحكم فيها بنظام إدارة الحقوق الرقمية، يجب تضمين الطرق الجديدة إلى جانب التدفق المعتاد لاستدعاءات MediaPlayer، كما هو موضّح أدناه:
Kotlin
mediaPlayer?.apply { setDataSource() setOnDrmConfigHelper() // optional, for custom configuration prepare() drmInfo?.also { prepareDrm() getKeyRequest() provideKeyResponse() } // MediaPlayer is now ready to use start() // ...play/pause/resume... stop() releaseDrm() }
Java
setDataSource(); setOnDrmConfigHelper(); // optional, for custom configuration prepare(); if (getDrmInfo() != null) { prepareDrm(); getKeyRequest(); provideKeyResponse(); } // MediaPlayer is now ready to use start(); // ...play/pause/resume... stop(); releaseDrm();
ابدأ بإعداد الكائن MediaPlayer
وضبط مصدره باستخدام setDataSource()
كالعادة. لاستخدام نظام إدارة الحقوق الرقمية بعد ذلك، عليك اتّباع الخطوات التالية:
- إذا أردت أن ينفِّذ تطبيقك إعدادات مخصّصة، حدِّد واجهة
OnDrmConfigHelper
وأرفقها بالمشغّل باستخدامsetOnDrmConfigHelper()
. - الاتصال بالرقم
prepare()
. - الاتصال بالرقم
getDrmInfo()
. إذا كان المصدر يشتمل على محتوى DRM، تعرض الطريقة قيمةMediaPlayer.DrmInfo
غير فارغة.
إذا كان MediaPlayer.DrmInfo
متاحًا:
- تحقَّق من خريطة أرقام التعريف الفريدة العالمية (UUID) المتوفرة واختَر إحداها.
- عليك تحضير إعدادات إدارة الحقوق الرقمية للمصدر الحالي من خلال استدعاء
prepareDrm()
. - إذا أنشأت وسجّلت معاودة الاتصال بالرمز
OnDrmConfigHelper
، يتم استدعائها أثناء تنفيذprepareDrm()
. ويتيح لك ذلك تنفيذ إعدادات مخصّصة لخصائص DRM قبل فتح جلسة DRM. ويتم استدعاء دالة معاودة الاتصال بشكل متزامن في سلسلة المحادثات التي تُسمىprepareDrm()
. للوصول إلى سمات DRM، اطلبgetDrmPropertyString()
وsetDrmPropertyString()
. تجنَّب تنفيذ عمليات طويلة. - في حال لم يتم توفير المتطلبات اللازمة للجهاز بعد،
يصل
prepareDrm()
أيضًا إلى خادم توفير المتطلبات اللازمة للجهاز لتوفير الخدمة. قد يستغرق ذلك مقدارًا متغيرًا من الوقت، وذلك حسب اتصال الشبكة. - للحصول على مصفوفة بايت لطلب مفتاح مبهم لإرسالها إلى خادم ترخيص، استدعِ
getKeyRequest()
. - لإبلاغ محرّك إدارة الحقوق الرقمية باستجابة المفتاح التي تم تلقّيها من خادم الترخيص، يمكنك طلب الرمز البرمجي
provideKeyResponse()
. وتعتمد النتيجة على نوع طلب المفتاح:- إذا كانت الاستجابة لطلب مفتاح بلا اتصال بالإنترنت، تكون النتيجة معرّف مجموعة مفاتيح. يمكنك استخدام
هذا المعرّف الذي تم ضبطه من خلال مجموعة المفاتيح مع
restoreKeys()
لاستعادة المفاتيح في جلسة جديدة. - إذا كان الردّ متعلقًا بطلب بث أو إصدار، تكون النتيجة فارغة.
- إذا كانت الاستجابة لطلب مفتاح بلا اتصال بالإنترنت، تكون النتيجة معرّف مجموعة مفاتيح. يمكنك استخدام
هذا المعرّف الذي تم ضبطه من خلال مجموعة المفاتيح مع
تشغيل prepareDrm()
بشكل غير متزامن
يتم تلقائيًا تشغيل prepareDrm()
بشكل متزامن، ويتم الحظر حتى انتهاء عملية الإعداد. ومع ذلك، قد يتطلّب أيضًا إعداد إدارة الحقوق الرقمية للمرة الأولى على جهاز جديد توفير المتطلبات اللازمة، حيث تتم
معالجة هذه العملية داخليًا من قِبل
"prepareDrm()
"، وقد يستغرق انتهاء العملية بعض الوقت بسبب عملية الشبكة التي تتضمّنها. ويمكنك
تجنُّب الحظر على
prepareDrm()
من خلال تحديد MediaPlayer.OnDrmPreparedListener
وضبطه.
عند ضبط OnDrmPreparedListener
،
يجري prepareDrm()
عملية توفير المتطلبات اللازمة (إذا لزم الأمر) والتحضير في الخلفية. عند الانتهاء من
التوفير والإعداد، يتم استدعاء المستمع. يُفترض ألا تضع أي افتراض بشأن تسلسل الاستدعاءات أو سلسلة التعليمات التي يتم تشغيل
المستمع فيها (إلا إذا كان المستمع مسجّلاً في سلسلة محادثات معالج).
يمكن استدعاء المستمع قبل
عودة "prepareDrm()
"
أو بعده.
إعداد إدارة الحقوق الرقمية بشكل غير متزامن
يمكنك إعداد إدارة الحقوق الرقمية بشكل غير متزامن من خلال إنشاء وتسجيل
MediaPlayer.OnDrmInfoListener
لإعداد نظام إدارة الحقوق الرقمية
وMediaPlayer.OnDrmPreparedListener
لبدء تشغيل المشغِّل.
وهي تعمل جنبًا إلى جنب مع
prepareAsync()
، كما هو موضّح أدناه:
Kotlin
setOnPreparedListener() setOnDrmInfoListener() setDataSource() prepareAsync() // ... // If the data source content is protected you receive a call to the onDrmInfo() callback. override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) { mediaPlayer.apply { prepareDrm() getKeyRequest() provideKeyResponse() } } // When prepareAsync() finishes, you receive a call to the onPrepared() callback. // If there is a DRM, onDrmInfo() sets it up before executing this callback, // so you can start the player. override fun onPrepared(mediaPlayer: MediaPlayer) { mediaPlayer.start() }
Java
setOnPreparedListener(); setOnDrmInfoListener(); setDataSource(); prepareAsync(); // ... // If the data source content is protected you receive a call to the onDrmInfo() callback. onDrmInfo() { prepareDrm(); getKeyRequest(); provideKeyResponse(); } // When prepareAsync() finishes, you receive a call to the onPrepared() callback. // If there is a DRM, onDrmInfo() sets it up before executing this callback, // so you can start the player. onPrepared() { start(); }
التعامل مع الوسائط المشفرة
بدءًا من الإصدار Android 8.0 (مستوى واجهة برمجة التطبيقات 26)، بإمكان "MediaPlayer
" أيضًا فك تشفير
نظام التشفير المشترك (CENC)
و
HLS كان يتم في السابق إتاحة الوسائط المشفّرة ذات المقطع الكامل (METHOD=AES-128).
استرداد الوسائط من برنامج ContentSolver
وهناك ميزة أخرى قد تكون مفيدة في تطبيق مشغّل الوسائط وهي إمكانية استرداد الموسيقى الموجودة لدى المستخدم على الجهاز. يمكنك إجراء ذلك من خلال الاستعلام عن ContentResolver
للوسائط الخارجية:
Kotlin
val resolver: ContentResolver = contentResolver val uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI val cursor: Cursor? = resolver.query(uri, null, null, null, null) when { cursor == null -> { // query failed, handle error. } !cursor.moveToFirst() -> { // no media on the device } else -> { val titleColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE) val idColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID) do { val thisId = cursor.getLong(idColumn) val thisTitle = cursor.getString(titleColumn) // ...process entry... } while (cursor.moveToNext()) } } cursor?.close()
Java
ContentResolver contentResolver = getContentResolver(); Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Cursor cursor = contentResolver.query(uri, null, null, null, null); if (cursor == null) { // query failed, handle error. } else if (!cursor.moveToFirst()) { // no media on the device } else { int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE); int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID); do { long thisId = cursor.getLong(idColumn); String thisTitle = cursor.getString(titleColumn); // ...process entry... } while (cursor.moveToNext()); }
لاستخدام هذا الإجراء مع MediaPlayer
، يمكنك اتّباع الخطوات التالية:
Kotlin
val id: Long = /* retrieve it from somewhere */ val contentUri: Uri = ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id ) mediaPlayer = MediaPlayer().apply { setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(applicationContext, contentUri) } // ...prepare and start...
Java
long id = /* retrieve it from somewhere */; Uri contentUri = ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioAttributes( new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(getApplicationContext(), contentUri); // ...prepare and start...
مزيد من المعلومات
تتناول هذه الصفحات مواضيع تتعلق بتسجيل الصوت والفيديو وتخزينهما وتشغيلهما.