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

يتم تحديد واجهة برمجة التطبيقات الخاصة بقائمة التشغيل من خلال واجهة 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 نفسها عدة مرات في قائمة تشغيل.

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

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

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. تشمل الأسباب الأخرى التي قد تؤدي إلى تعديل الجدول الزمني ما يلي:

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