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

يتم تحديد واجهة برمجة التطبيقات الخاصة بقوائم التشغيل من خلال الواجهة 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 ذات الصلة بالتشغيل. على سبيل المثال، يمكن تعديل حقول 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. تشمل الأسباب الأخرى التي يمكن أن تؤدي إلى تعديل المخطط الزمني ما يلي:

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