מדיה זורמת

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.getCurrentMediaItemIndex ו-Timeline.getWindow. בתוך Window:
    • Window.liveConfiguration מכיל את הפרמטרים של היעד של ההיסט בשידור החי וההתאמה של ההיסט בשידור החי. הערכים האלה מבוססים על מידע במדיה ועל ההגדרות החלופיות שסופקו על ידי האפליקציה ומוגדרות ב-MediaItem.liveConfiguration.
    • Window.windowStartTimeMs הוא הזמן מאז חותמת הזמן של Unix שבו מתחיל חלון החיפוש.
    • Window.getCurrentUnixTimeMs הוא הזמן מאז ראשית זמן יוניקס (Unix epoch) של הזמן הנוכחי בזמן אמת. אפשר לתקן את הערך הזה לפי הפרש ידוע בין השעון בשרת לבין השעון בלקוח.
    • 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
  }
}