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 epoch) שבו מתחיל חלון השידור החי. -
Window.getCurrentUnixTimeMsהוא הזמן שחלף מאז ראשית זמן יוניקס (Unix epoch) של הזמן הנוכחי בזמן אמת. יכול להיות שהערך הזה יתוקן לפי הפרש ידוע בין השעון של השרת לשעון של הלקוח. -
Window.getDefaultPositionMsהוא המיקום בחלון השידור החי שבו הנגן יתחיל את ההפעלה כברירת מחדל.
- השדה
דילוג בשידורים חיים
אפשר לדלג לכל נקודה בחלון השידור החי באמצעות Player.seekTo. מיקום החיפוש שמועבר הוא ביחס לתחילת חלון השידור החי. לדוגמה, הערך seekTo(0) יגרום לדילוג לתחילת חלון השידור החי. הנגן ינסה לשמור על אותו היסט של השידור החי כמו המיקום שאליו בוצע הדילוג אחרי הדילוג.
לחלון השידור החי יש גם מיקום ברירת מחדל שבו ההפעלה אמורה להתחיל. המיקום הזה בדרך כלל קרוב לנקודת השידור החי. אפשר לדלג למיקום ברירת המחדל באמצעות קריאה ל-Player.seekToDefaultPosition.
ממשק משתמש להפעלה בשידור חי
רכיבי ממשק המשתמש שמוגדרים כברירת מחדל ב-ExoPlayer מציגים את משך הזמן של חלון השידור החי ואת מיקום ההפעלה הנוכחי בתוכו. כלומר, נראה שהמיקום קופץ אחורה בכל פעם שחלון השידור החי מתעדכן. אם אתם צריכים התנהגות אחרת, למשל להציג את ראשית זמן יוניקס (Unix epoch) או את ההיסט הנוכחי של השידור החי, אתם יכולים ליצור עותק של 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
מיקום ההפעלה עלול לחרוג מטווח השידור החי, למשל אם הנגן מושהה או מבצע אגירת נתונים (buffering) למשך זמן ארוך מספיק. אם זה יקרה, ההפעלה תיכשל ותדווח חריגה עם קוד השגיאה 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 } }