نظرة عامة على MediaPlayer

يتضمن إطار عمل الوسائط المتعددة في 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() كالعادة. لاستخدام نظام إدارة الحقوق الرقمية بعد ذلك، عليك اتّباع الخطوات التالية:

  1. إذا أردت أن ينفِّذ تطبيقك إعدادات مخصّصة، حدِّد واجهة OnDrmConfigHelper وأرفقها بالمشغّل باستخدام setOnDrmConfigHelper().
  2. الاتصال بالرقم prepare().
  3. الاتصال بالرقم getDrmInfo(). إذا كان المصدر يشتمل على محتوى DRM، تعرض الطريقة قيمة MediaPlayer.DrmInfo غير فارغة.

إذا كان MediaPlayer.DrmInfo متاحًا:

  1. تحقَّق من خريطة أرقام التعريف الفريدة العالمية (UUID) المتوفرة واختَر إحداها.
  2. عليك تحضير إعدادات إدارة الحقوق الرقمية للمصدر الحالي من خلال استدعاء prepareDrm().
    • إذا أنشأت وسجّلت معاودة الاتصال بالرمز OnDrmConfigHelper، يتم استدعائها أثناء تنفيذ prepareDrm(). ويتيح لك ذلك تنفيذ إعدادات مخصّصة لخصائص DRM قبل فتح جلسة DRM. ويتم استدعاء دالة معاودة الاتصال بشكل متزامن في سلسلة المحادثات التي تُسمى prepareDrm(). للوصول إلى سمات DRM، اطلب getDrmPropertyString() وsetDrmPropertyString(). تجنَّب تنفيذ عمليات طويلة.
    • في حال لم يتم توفير المتطلبات اللازمة للجهاز بعد، يصل prepareDrm() أيضًا إلى خادم توفير المتطلبات اللازمة للجهاز لتوفير الخدمة. قد يستغرق ذلك مقدارًا متغيرًا من الوقت، وذلك حسب اتصال الشبكة.
  3. للحصول على مصفوفة بايت لطلب مفتاح مبهم لإرسالها إلى خادم ترخيص، استدعِ getKeyRequest().
  4. لإبلاغ محرّك إدارة الحقوق الرقمية باستجابة المفتاح التي تم تلقّيها من خادم الترخيص، يمكنك طلب الرمز البرمجي 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...

مزيد من المعلومات

تتناول هذه الصفحات مواضيع تتعلق بتسجيل الصوت والفيديو وتخزينهما وتشغيلهما.