البث المباشر

يشغِّل ExoPlayer معظم أحداث البث المباشر التكيُّفية بطريقة مبتكرة بدون الحاجة إلى ضبط إعدادات خاصة. راجِع صفحة التنسيقات المتوافقة للحصول على مزيد من التفاصيل.

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

رصد عمليات التشغيل المباشر ومراقبتها

في كل مرة يتم فيها تعديل نافذة بث مباشر، ستتلقّى أحداث Player.Listener المسجَّلة حدث onTimelineChanged. يمكنك استرداد تفاصيل حول البث المباشر الحالي من خلال الاستعلام عن طرق Player وTimeline.Window مختلفة، كما هو موضّح أدناه والموضّح في الشكل التالي.

نافذة البث المباشر

  • يشير الرمز Player.isCurrentWindowLive إلى ما إذا كان عنصر الوسائط الذي يتم تشغيله حاليًا عبارة عن بث مباشر. وتظل هذه القيمة صحيحة حتى إذا انتهى البث المباشر.
  • تشير السمة Player.isCurrentWindowDynamic إلى ما إذا كان عنصر الوسائط الذي يتم تشغيله حاليًا لا يزال قيد التحديث. ينطبق ذلك عادةً على أحداث البث المباشر التي لم تنتهِ بعد. تنطبق هذه العلامة أيضًا على أحداث البث غير المباشر في بعض الحالات.
  • تعرض Player.getCurrentLiveOffset القيمة الإزاحة بين الوقت الفعلي الحالي وموضع التشغيل (إذا كان متاحًا).
  • تعرض القيمة Player.getDuration طول نافذة البث المباشر الحالية.
  • ويعرض Player.getCurrentPosition موضع التشغيل بالنسبة إلى بداية نافذة البث المباشر.
  • تعرض Player.getCurrentMediaItem عنصر الوسائط الحالي، حيث يحتوي MediaItem.liveConfiguration على عمليات إلغاء يقدّمها التطبيق لمَعلمات التعويض المباشر المستهدف وتسوية الإزاحة المباشرة.
  • تعرض دالة Player.getCurrentTimeline بنية الوسائط الحالية في Timeline. يمكن استرداد Timeline.Window الحالية من Timeline باستخدام Player.getCurrentWindowIndex وTimeline.getWindow. ضمن Window:
    • تحتوي السمة Window.liveConfiguration على مَعلمات تعديل الإزاحة المباشرة المستهدفة والإزاحة المباشرة. تستند هذه القيم إلى المعلومات الواردة في الوسائط وأي عمليات إلغاء يقدّمها التطبيق تم ضبطها في MediaItem.liveConfiguration.
    • Window.windowStartTimeMs هو الوقت منذ حقبة Unix التي تبدأ فيها نافذة البث المباشر.
    • Window.getCurrentUnixTimeMs هو الوقت منذ حقبة Unix في الوقت الفعلي الحالي. يمكن تصحيح هذه القيمة من خلال اختلاف معروف في الساعة بين الخادم والعميل.
    • Window.getDefaultPositionMs هو الموضع في نافذة البث المباشر الذي سيبدأ عنده المشغّل تلقائيًا بتشغيل المحتوى.

تقديم المحتوى في أحداث البث المباشر

يمكنك الانتقال إلى أي مكان داخل النافذة المباشرة باستخدام Player.seekTo. يرتبط موضع التقديم الذي تم تمريره ببداية النافذة المباشرة. على سبيل المثال، سينتقل seekTo(0) إلى بداية النافذة المنشورة. سيحاول اللاعب الحفاظ على الإزاحة المباشرة نفسها مثل الموضع المطلوب تحقيقه بعد التقديم/الترجيع.

تحتوي نافذة البث المباشر أيضًا على موضع تلقائي من المفترض أن يبدأ فيه التشغيل. يكون هذا الموضع عادةً قريبًا من الحافة المباشرة. يمكنك الانتقال إلى الموضع التلقائي من خلال طلب Player.seekToDefaultPosition.

واجهة مستخدم التشغيل المباشر

تعرض مكونات واجهة المستخدم التلقائية في ExoPlayer مدة عرض البث المباشر وموضع التشغيل الحالي فيها. هذا يعني أن الموضع سيظهر في رجوع إلى الوراء في كل مرة يتم فيها تحديث النافذة المباشرة. إذا كنت بحاجة إلى سلوك مختلف، على سبيل المثال عرض وقت Unix أو المعادلة المباشرة الحالية، يمكنك استخدام رمز PlayerControlView وتعديله بما يتناسب مع احتياجاتك.

ضبط معلمات التشغيل المباشر

يستخدم ExoPlayer بعض المعلمات للتحكّم في إزاحة موضع التشغيل من الحافة المباشرة، ونطاق سرعات التشغيل التي يمكن استخدامها لضبط هذه الإزاحة.

يحصل ExoPlayer على قيم هذه المعلمات من ثلاثة أماكن بترتيب تنازلي للأولوية (يتم استخدام القيمة الأولى التي تم العثور عليها):

  • لكل MediaItem قيمة تم تمريرها إلى MediaItem.Builder.setLiveConfiguration.
  • تم ضبط القيم التلقائية العامة في DefaultMediaSourceFactory.
  • تتم قراءة القيم مباشرةً من الوسائط.

Kotlin

// Global settings.
val player =
  ExoPlayer.Builder(context)
    .setMediaSourceFactory(DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
    .build()

// Per MediaItem settings.
val mediaItem =
  MediaItem.Builder()
    .setUri(mediaUri)
    .setLiveConfiguration(
      MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build()
    )
    .build()
player.setMediaItem(mediaItem)

Java

// Global settings.
ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(context).setLiveTargetOffsetMs(5000))
        .build();

// Per MediaItem settings.
MediaItem mediaItem =
    new MediaItem.Builder()
        .setUri(mediaUri)
        .setLiveConfiguration(
            new MediaItem.LiveConfiguration.Builder().setMaxPlaybackSpeed(1.02f).build())
        .build();
player.setMediaItem(mediaItem);

قيم الضبط المتاحة هي:

  • targetOffsetMs: المحتوى المباشر المستهدَف وسيحاول المشغّل الاقتراب من هذه الإزاحة المباشرة أثناء التشغيل إن أمكن.
  • minOffsetMs: الحدّ الأدنى المسموح به للإزاحة المباشرة حتى عند ضبط الإزاحة حسب ظروف الشبكة الحالية، لن يحاول المشغّل الوصول إلى قيمة أقل من هذا الإزاحة أثناء التشغيل.
  • maxOffsetMs: الحدّ الأقصى للإزاحة المباشرة المسموح بها حتى عند ضبط الإزاحة مع ظروف الشبكة الحالية، لن يحاول المشغّل تجاوز هذا الإزاحة أثناء التشغيل.
  • minPlaybackSpeed: الحد الأدنى لسرعة التشغيل التي يمكن للمشغّل استخدامها للتراجع عند محاولة الوصول إلى القيمة المستهدَفة للبث المباشر
  • maxPlaybackSpeed: الحد الأقصى لسرعة التشغيل التي يمكن للمشغّل استخدامها لمواكبة السرعة المستهدَفة في البث المباشر

تعديل سرعة التشغيل

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

في حال لم يكن من المطلوب ضبط سرعة التشغيل التلقائي، يمكن إيقافها من خلال ضبط السمتَين minPlaybackSpeed وmaxPlaybackSpeed على 1.0f. وبالمثل، يمكن تفعيل هذه الميزة لأحداث البث المباشر التي لا تتطلب وقت استجابة بطيء من خلال ضبطها بوضوح على قيم أخرى غير 1.0f. راجع قسم التهيئة أعلاه للحصول على المزيد من التفاصيل حول كيفية ضبط هذه الخصائص.

تخصيص خوارزمية تعديل سرعة التشغيل

في حال تفعيل تعديل السرعة، يحدِّد LivePlaybackSpeedControl التعديلات التي يتم إجراؤها. يمكن تنفيذ سمة LivePlaybackSpeedControl مخصّصة أو تخصيص عملية التنفيذ التلقائية، وهي DefaultLivePlaybackSpeedControl. في كلتا الحالتين، يمكن تعيين مثيل عند إنشاء المشغل:

Kotlin

val player =
  ExoPlayer.Builder(context)
    .setLivePlaybackSpeedControl(
      DefaultLivePlaybackSpeedControl.Builder().setFallbackMaxPlaybackSpeed(1.04f).build()
    )
    .build()

Java

ExoPlayer player =
    new ExoPlayer.Builder(context)
        .setLivePlaybackSpeedControl(
            new DefaultLivePlaybackSpeedControl.Builder()
                .setFallbackMaxPlaybackSpeed(1.04f)
                .build())
        .build();

في ما يلي مَعلمات التخصيص ذات الصلة بميزة "DefaultLivePlaybackSpeedControl":

  • fallbackMinPlaybackSpeed وfallbackMaxPlaybackSpeed: الحد الأدنى والأقصى لسرعات التشغيل التي يمكن استخدامها للتعديل إذا لم تحدد الوسائط أو MediaItem التي يوفرها التطبيق حدودًا
  • proportionalControlFactor: تتحكّم هذه السمة في مدى سلاسة تعديل السرعة، إذ تؤدي القيمة العالية إلى إجراء تعديلات مفاجئة وتفاعلية بشكل أكبر، ويزداد احتمال أن تكون مسموعة. تؤدي القيمة الأصغر إلى انتقال أكثر سلاسة بين السرعات، على حساب أن تكون أبطأ.
  • targetLiveOffsetIncrementOnRebufferMs: تتم إضافة هذه القيمة إلى قيمة التعويض المباشر المستهدف كلما حدث مورد احتياطي، من أجل المتابعة بحذر أكبر. يمكن إيقاف هذه الميزة من خلال ضبط القيمة على 0.
  • minPossibleLiveOffsetSmoothingFactor: عامل تجانس أُسيّ يُستخدم لتتبُّع الحدّ الأدنى المحتمل للإزاحة المباشرة استنادًا إلى الوسائط المخزَّنة مؤقتًا في الوقت الحالي. تعني القيمة القريبة جدًا من 1 أن التقدير أكثر حذرًا وقد يستغرق وقتًا أطول للتكيف مع ظروف الشبكة المحسّنة، في حين تعني القيمة الأقل أنه سيتم ضبط التقدير بشكل أسرع مع وجود خطر أكبر للوقوع في الموارد الاحتياطية.

BehindLiveWindowException وERROR_CODE_BEHIND_LIVE_WINDOW

قد يظهر موضع التشغيل خلف نافذة البث المباشر، مثلاً عندما يكون المشغّل متوقفًا مؤقتًا أو يتوقف مؤقتًا لفترة زمنية كافية. في حال حدوث ذلك، ستتعذّر عملية التشغيل وسيتم الإبلاغ عن استثناء يتضمّن رمز الخطأ ERROR_CODE_BEHIND_LIVE_WINDOW عبر Player.Listener.onPlayerError. قد يحتاج كود التطبيق إلى معالجة مثل هذه الأخطاء من خلال استئناف التشغيل بالوضع الافتراضي. يلخص PlayerActivity في التطبيق التجريبي هذا النهج.

Kotlin

override fun onPlayerError(error: PlaybackException) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition()
    player.prepare()
  } else {
    // Handle other errors
  }
}

Java

@Override
public void onPlayerError(PlaybackException error) {
  if (error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) {
    // Re-initialize player at the live edge.
    player.seekToDefaultPosition();
    player.prepare();
  } else {
    // Handle other errors
  }
}