يتضمن إطار عمل الوسائط المتعددة في 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) الداخلية، مثل التي قد تحصل عليها من أداة حل المحتوى
- عناوين 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
في هذه الحالة، تشير كلمة "Raw" هو ملف لا يستخدمه النظام محاولة التحليل بأي طريقة معينة. ومع ذلك، لا ينبغي أن يتضمن محتوى هذا المورد تكون ذات صوت أولي. يجب أن يكون ملف وسائط مُرمَّزًا ومنسّقًا بشكل صحيح في ملف واحد من التنسيقات المتوافقة
وإليك كيفية التشغيل من عنوان 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
جديدة لم يسبق لك
. (لمزيد من المعلومات حول عمليات إعادة التشغيل في بيئة التشغيل، يُرجى الاطّلاع على التعامل مع التغييرات في بيئة التشغيل).
قد تتساءل ماذا يحدث إذا أردت مواصلة اللعب
"وسائط الخلفية" حتى عندما يغادر المستخدم نشاطك، يكون الأمر كذلك إلى حد كبير
الذي يتصرف به تطبيق Music المدمَج. في هذه الحالة، ما تحتاجه هو
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
.
إلى حالة خطأ (يمكنك مراجعة وثائق
الفئة 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);
ومع ذلك، لا يضمن قفل التنشيط المكتسب في هذا المثال أن تظل وحدة المعالجة المركزية في الوضع النشط. في حال حذف
تقوم ببث الوسائط عبر
شبكة Wi-Fi وكنت تستخدم شبكة 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)
بدءًا من الإصدار 8.0 من نظام التشغيل Android (المستوى 26 من واجهة برمجة التطبيقات)، سيشمل MediaPlayer
واجهات برمجة تطبيقات
إتاحة تشغيل مواد محمية بموجب إدارة الحقوق الرقمية وهي تشبه واجهة برمجة التطبيقات منخفضة المستوى التي توفرها
MediaDrm
، لكنها تعمل على مستوى أعلى ولا
الكشف عن أداة الاستخراج الأساسية وكائنات drm وغيرها من كائنات التشفير.
وعلى الرغم من أن واجهة برمجة التطبيقات MediaPlayer DRM لا توفر الوظائف الكاملة
MediaDrm
، وهي متوافقة مع حالات الاستخدام الأكثر شيوعًا. تشير رسالة الأشكال البيانية
في ما يخص عملية التنفيذ الحالية، يمكن التعامل مع أنواع المحتوى التالية:
- ملفات الوسائط المحلية المحمية من خلال Widevine
- ملفات وسائط البث/التحكّم عن بُعد المحمية من خلال Widevine
يوضح مقتطف الرمز التالي كيفية استخدام برنامج 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()
،
كالمعتاد. بعد ذلك، لاستخدام إدارة الحقوق الرقمية (DRM)، يمكنك تنفيذ الخطوات التالية:
- إذا كنت تريد أن يؤدي تطبيقك ضبطًا مخصّصًا، حدِّد
OnDrmConfigHelper
، ثم أرفقه لاعب يستخدمsetOnDrmConfigHelper()
- الاتصال بالرقم
prepare()
. - الاتصال بالرقم
getDrmInfo()
. إذا كان المصدر يتضمن إدارة الحقوق الرقمية المحتوى، تُرجع الطريقة قيمة غير خالية قيمةMediaPlayer.DrmInfo
.
في حال توفُّر MediaPlayer.DrmInfo
:
- افحص خريطة المعرّف الفريد العالمي (UUID) المتاحة واختَر أحدها.
- يمكنك إعداد إعدادات إدارة الحقوق الرقمية للمصدر الحالي من خلال طلب الرقم
prepareDrm()
. - إذا قمت بإنشاء وتسجيل
اسم معاودة الاتصال
OnDrmConfigHelper
، يُسمى بينماprepareDrm()
قيد التنفيذ. يتيح لك هذا الإجراء ضبط إعدادات مخصّصة لإدارة الحقوق الرقمية قبل فتح جلسة إدارة الحقوق الرقمية. تُسمى معاودة الاتصال بشكل متزامن في سلسلة المحادثات التي تستدعي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 (الطريقة=SAMPLE-AES) لأنواع البث الأولية
H.264 وAAC كان يتم في السابق إتاحة الوسائط المشفَّرة ضمن المقطع بالكامل (method=AES-128).
جارٍ استرداد الوسائط من محلل محتوى
ومن الميزات الأخرى التي قد تكون مفيدة في تطبيق مشغّل الوسائط القدرة على
استرداد الموسيقى الموجودة على الجهاز لدى المستخدم. يمكنك إجراء ذلك من خلال إرسال طلب بحث عن "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...
مزيد من المعلومات
تتناول هذه الصفحات المواضيع المتعلقة بتسجيل الصوت والفيديو وتخزينهما وتشغيلهما.