نظرة عامة على 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) الداخلية، مثل التي قد تحصل عليها من أداة حل المحتوى
  • عناوين 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)، يمكنك تنفيذ الخطوات التالية:

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

في حال توفُّر MediaPlayer.DrmInfo:

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

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

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