قوائم التشغيل

يتم تعريف واجهة برمجة التطبيقات الخاصة بالقوائم الموسيقية من خلال واجهة Player التي يتم تنفيذها من خلال جميع عمليات تنفيذ ExoPlayer. تتيح قوائم التشغيل تشغيل محتوى وسائط متعدّد بالتسلسل. يوضّح المثال التالي كيفية بدء تشغيل قائمة تشغيل تحتوي على فيديوهَين:

Kotlin

// Build the media items.
val firstItem = MediaItem.fromUri(firstVideoUri)
val secondItem = MediaItem.fromUri(secondVideoUri)
// Add the media items to be played.
player.addMediaItem(firstItem)
player.addMediaItem(secondItem)
// Prepare the player.
player.prepare()
// Start the playback.
player.play()

Java

// Build the media items.
MediaItem firstItem = MediaItem.fromUri(firstVideoUri);
MediaItem secondItem = MediaItem.fromUri(secondVideoUri);
// Add the media items to be played.
player.addMediaItem(firstItem);
player.addMediaItem(secondItem);
// Prepare the player.
player.prepare();
// Start the playback.
player.play();

تكون عمليات الانتقال بين العناصر في قائمة التشغيل سلسة. ولا يُشترط أن تكون الفيديوهات بالتنسيق نفسه (على سبيل المثال، لا بأس أن تتضمّن قائمة التشغيل كلاً من فيديوهات H264 وفيديوهات VP9). وقد تكون أيضًا من أنواع مختلفة (أي أنّه لا بأس أن تحتوي قائمة التشغيل على فيديوهات وصور وعمليات بث صوتية فقط). ويمكنك استخدام السمة MediaItem نفسها عدة مرات ضمن قائمة التشغيل.

تعديل قائمة التشغيل

يمكنك تعديل قائمة تشغيل بشكل ديناميكي من خلال إضافة عناصر وسائط أو نقلها أو إزالتها أو استبدالها. ويمكن إجراء ذلك قبل التشغيل وأثناءه من خلال استدعاء methods المقابلة لواجهة برمجة تطبيقات قائمة التشغيل:

Kotlin

// Adds a media item at position 1 in the playlist.
player.addMediaItem(/* index= */ 1, MediaItem.fromUri(thirdUri))
// Moves the third media item from position 2 to the start of the playlist.
player.moveMediaItem(/* currentIndex= */ 2, /* newIndex= */ 0)
// Removes the first item from the playlist.
player.removeMediaItem(/* index= */ 0)
// Replace the second item in the playlist.
player.replaceMediaItem(/* index= */ 1, MediaItem.fromUri(newUri))

Java

// Adds a media item at position 1 in the playlist.
player.addMediaItem(/* index= */ 1, MediaItem.fromUri(thirdUri));
// Moves the third media item from position 2 to the start of the playlist.
player.moveMediaItem(/* currentIndex= */ 2, /* newIndex= */ 0);
// Removes the first item from the playlist.
player.removeMediaItem(/* index= */ 0);
// Replace the second item in the playlist.
player.replaceMediaItem(/* index= */ 1, MediaItem.fromUri(newUri));

يمكنك أيضًا استبدال قائمة التشغيل بأكملها أو محو المحتوى منها:

Kotlin

// Replaces the playlist with a new one.
val newItems: List<MediaItem> = listOf(MediaItem.fromUri(fourthUri), MediaItem.fromUri(fifthUri))
player.setMediaItems(newItems, /* resetPosition= */ true)
// Clears the playlist. If prepared, the player transitions to the ended state.
player.clearMediaItems()

Java

// Replaces the playlist with a new one.
ImmutableList<MediaItem> newItems =
    ImmutableList.of(MediaItem.fromUri(fourthUri), MediaItem.fromUri(fifthUri));
player.setMediaItems(newItems, /* resetPosition= */ true);
// Clears the playlist. If prepared, the player transitions to the ended state.
player.clearMediaItems();

يعالج المشغّل التعديلات تلقائيًا أثناء التشغيل بالطريقة الصحيحة:

  • في حال نقل جهاز MediaItem قيد التشغيل حاليًا، لن تتم مقاطعة التشغيل وسيتم تشغيل المحتوى التالي عند الانتهاء.
  • إذا تمت إزالة MediaItem الذي يتم تشغيله حاليًا، سيشغّل المشغّل تلقائيًا المحتوى التالي المتبقّي، أو سينتقل إلى الحالة "انتهى" في حال عدم توفّر محتوى لاحق.
  • في حال استبدال MediaItem المشغّل حاليًا، لن يتم إيقاف التشغيل إذا لم يتغيّر أيّ من السمات في MediaItem ذات الصلة بالتشغيل. على سبيل المثال، من الممكن تعديل حقول MediaItem.MediaMetadata في معظم الحالات بدون التأثير في التشغيل.

الاستعلام عن قائمة التشغيل

يمكن الاستعلام عن قائمة التشغيل باستخدام Player.getMediaItemCount و Player.getMediaItemAt. يمكن الاستعلام عن ملف الوسائط الذي يتم تشغيله حاليًا من خلال الاتصال بالرقم Player.getCurrentMediaItem. تتوفر أيضًا طُرق سهلة أخرى مثل Player.hasNextMediaItem أو Player.getNextMediaItemIndex لتبسيط التنقّل في قائمة التشغيل.

أوضاع التكرار

يتيح المشغّل 3 أوضاع تكرار يمكن ضبطها في أي وقت باستخدام Player.setRepeatMode:

  • Player.REPEAT_MODE_OFF: لا تتم إعادة تشغيل قائمة التشغيل وسينتقل المشغّل إلى Player.STATE_ENDED بعد تشغيل العنصر الأخير في قائمة التشغيل.
  • Player.REPEAT_MODE_ONE: يتم تكرار العنصر الحالي في حلقة لا تنتهي. ستتجاهل طُرق مثل Player.seekToNextMediaItem هذا الأمر وستبحث عن العنصر التالي في القائمة، والذي سيتم تكراره بعد ذلك في حلقة لا تنتهي.
  • Player.REPEAT_MODE_ALL: يتم تكرار قائمة التشغيل بأكملها بشكل متكرّر.

وضع الترتيب العشوائي

يمكن تفعيل وضع "الترتيب العشوائي" أو إيقافه في أي وقت باستخدام الرمز Player.setShuffleModeEnabled. في وضع "الترتيب العشوائي"، سيشغّل المشغّل قائمة التشغيل بترتيب عشوائي تم احتسابه مسبقًا. سيتم تشغيل جميع العناصر مرة واحدة ويمكن أيضًا دمج وضع الترتيب العشوائي مع Player.REPEAT_MODE_ALL لتكرار الترتيب العشوائي نفسه في تكرار لا نهائي. عند إيقاف وضع "الترتيب العشوائي"، يستمر التشغيل من العنصر الحالي في موضعه الأصلي في قائمة التشغيل.

يُرجى العِلم أنّ الفهارس التي تعرضها طرق مثل Player.getCurrentMediaItemIndex تشير دائمًا إلى الترتيب الأصلي غير المُرتب. وبالمثل، لن يشغّل Player.seekToNextMediaItem العنصر في player.getCurrentMediaItemIndex() + 1، بل العنصر التالي وفقًا لترتيب التبديل العميق. سيؤدي إدراج عناصر جديدة في قائمة التشغيل أو إزالة عناصر إلى إبقاء الترتيب الحالي الذي تم ترتيبه عشوائيًا بدون تغيير قدر الإمكان.

ضبط ترتيب عكسي مخصّص

يتيح المشغّل بشكل تلقائي ترتيب الأغاني عشوائيًا باستخدام الرمز DefaultShuffleOrder. يمكن تخصيص ذلك من خلال توفير عملية تنفيذ مخصّصة لترتيب الترتيب العشوائي، أو من خلال ضبط ترتيب مخصّص في أداة إنشاء DefaultShuffleOrder:

Kotlin

// Set a custom shuffle order for the 5 items currently in the playlist:
exoPlayer.setShuffleOrder(DefaultShuffleOrder(intArrayOf(3, 1, 0, 4, 2), randomSeed))
// Enable shuffle mode.
exoPlayer.shuffleModeEnabled = true

Java

// Set a custom shuffle order for the 5 items currently in the playlist:
exoPlayer.setShuffleOrder(new DefaultShuffleOrder(new int[] {3, 1, 0, 4, 2}, randomSeed));
// Enable shuffle mode.
exoPlayer.setShuffleModeEnabled(/* shuffleModeEnabled= */ true);

تحديد عناصر قائمة التشغيل

لتحديد عناصر قائمة التشغيل، يمكن ضبط MediaItem.mediaId عند إنشاء العنصر:

Kotlin

// Build a media item with a media ID.
val mediaItem = MediaItem.Builder().setUri(uri).setMediaId(mediaId).build()

Java

// Build a media item with a media ID.
MediaItem mediaItem = new MediaItem.Builder().setUri(uri).setMediaId(mediaId).build();

إذا لم يُحدِّد التطبيق معرّف الوسائط بشكل صريح لعنصر من الوسائط، سيتم استخدام تمثيل السلسلة لمعرّف الموارد المنتظم (URI).

ربط بيانات التطبيق بعناصر قائمة التشغيل

بالإضافة إلى المعرّف، يمكن أيضًا ضبط كل عنصر وسائط باستخدام علامة مخصّصة، يمكن أن تكون أي عنصر يقدّمه التطبيق. من بين استخدامات العلامات المخصّصة إرفاق بيانات التعريف بكل عنصر وسائط:

Kotlin

// Build a media item with a custom tag.
val mediaItem = MediaItem.Builder().setUri(uri).setTag(metadata).build()

Java

// Build a media item with a custom tag.
MediaItem mediaItem = new MediaItem.Builder().setUri(uri).setTag(metadata).build();

رصد حالات انتقال التشغيل إلى عنصر وسائط آخر

عندما ينتقل التشغيل إلى عنصر وسائط آخر أو يبدأ في تكرار عنصر الوسائط نفسه، يتم استدعاء Listener.onMediaItemTransition(MediaItem, @MediaItemTransitionReason). يتلقّى هذا المرجع المرجعي العنصر الجديد في الوسائط، بالإضافة إلى @MediaItemTransitionReason يشير إلى سبب حدوث عملية التحويل. من حالات الاستخدام الشائعة لـ onMediaItemTransition تعديل واجهة مستخدم التطبيق لعرض عنصر الوسائط الجديد:

Kotlin

override fun onMediaItemTransition(
  mediaItem: MediaItem?,
  @MediaItemTransitionReason reason: Int,
) {
  updateUiForPlayingMediaItem(mediaItem)
}

Java

@Override
public void onMediaItemTransition(
    @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
  updateUiForPlayingMediaItem(mediaItem);
}

إذا تم إرفاق البيانات الوصفية المطلوبة لتعديل واجهة المستخدم بكل عنصر وسائط باستخدام علامات مخصّصة، قد يبدو التنفيذ على النحو التالي:

Kotlin

override fun onMediaItemTransition(
  mediaItem: MediaItem?,
  @MediaItemTransitionReason reason: Int,
) {
  var metadata: CustomMetadata? = null
  mediaItem?.localConfiguration?.let { localConfiguration ->
    metadata = localConfiguration.tag as? CustomMetadata
  }
  updateUiForPlayingMediaItem(metadata)
}

Java

@Override
public void onMediaItemTransition(
    @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) {
  @Nullable CustomMetadata metadata = null;
  if (mediaItem != null && mediaItem.localConfiguration != null) {
    metadata = (CustomMetadata) mediaItem.localConfiguration.tag;
  }
  updateUiForPlayingMediaItem(metadata);
}

رصد التغييرات في قائمة التشغيل

عند إضافة عنصر وسائط أو إزالته أو نقله، يتم استدعاء Listener.onTimelineChanged(Timeline, @TimelineChangeReason) على الفور باستخدام TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED. يتمّ استدعاء هذا المرجع الخلفي حتى إذا لم يتمّ إعداد المشغّل بعد.

Kotlin

override fun onTimelineChanged(timeline: Timeline, @TimelineChangeReason reason: Int) {
  if (reason == Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) {
    // Update the UI according to the modified playlist (add, move or remove).
    updateUiForPlaylist(timeline)
  }
}

Java

@Override
public void onTimelineChanged(Timeline timeline, @TimelineChangeReason int reason) {
  if (reason == TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED) {
    // Update the UI according to the modified playlist (add, move or remove).
    updateUiForPlaylist(timeline);
  }
}

عندما تصبح معلومات، مثل مدة عنصر وسائط في قائمة التشغيل، متوفرة، سيتم تعديل Timeline وسيتم استدعاء onTimelineChanged باستخدام TIMELINE_CHANGE_REASON_SOURCE_UPDATE. تشمل الأسباب الأخرى التي يمكن أن تؤدي إلى تعديل المخطط الزمني ما يلي:

  • بيان يصبح متاحًا بعد إعداد عنصر وسائط تكيُّفي.
  • بيان يتم تعديله بشكل دوري أثناء تشغيل بث مباشر