הממשק בליבה של ספריית ExoPlayer נמצא ב-Player
. Player
חושף פונקציונליות מסורתית של נגן מדיה ברמה גבוהה, כגון היכולת
אחסון זמני של מדיה, הפעלה, השהיה וחיפוש. הטמעת ברירת המחדל ExoPlayer
היא
שמטרתו להניח מעט הנחות לגבי (ולכן להטיל מעט הגבלות על)
סוג המדיה המושמעת, אופן האחסון ואופן האחסון שלה
שעבר עיבוד. במקום ליישם את הטעינה והרינדור של מדיה ישירות,
הטמעות של ExoPlayer
מאצילות את העבודה הזו לרכיבים שמוחדרים
כשנגן נוצר או כשמקורות מדיה חדשים מועברים לנגן.
הרכיבים המשותפים לכל ההטמעות של ExoPlayer
הם:
MediaSource
מופעים שמגדירים את המדיה להפעלה, טוענים את המדיה וגם שממנו ניתן לקרוא את המדיה שנטענה. נוצרת מכונה שלMediaSource
מ-MediaItem
שלMediaSource.Factory
בתוך הנגן. הן גם יכולות יועברו ישירות לנגן באמצעות API של פלייליסט המבוסס על מקור מדיה.- מכונה של
MediaSource.Factory
שממירה אתMediaItem
ל-MediaSource
. החדרMediaSource.Factory
מועבר כשהנגן נוצר. - מכונות
Renderer
שמבצעות רינדור של רכיבים בודדים במדיה. הנושאים האלה מועבר כשהנגן נוצר. TrackSelector
שבוחר את הטראקים שסופקו על ידיMediaSource
נצרכים על ידי כלRenderer
זמין. החדרתTrackSelector
כשהנגן נוצר.LoadControl
שקובע כשה-MediaSource
מאחסן יותר מדיה, וגם כמות המדיה בתהליך אגירת נתונים. החדרLoadControl
מועבר כשהשחקן: נוצר.LivePlaybackSpeedControl
ששולט במהירות ההפעלה במהלך השידור החי הפעלות כדי לאפשר לנגן להישאר קרוב להיסט בשידור חי שהוגדר. א' החדרLivePlaybackSpeedControl
מועבר כשהנגן נוצר.
הקונספט של החדרת רכיבים שמטמיעים חלקים של נגן קיימת בכל הספרייה. יישומי ברירת המחדל של חלק מהרכיבים שמעבירים פועלים לרכיבים נוספים להחדרה. כך יכולים גורמים רבים שיוחלפו בנפרד בהטמעות באופן מותאם אישית.
התאמה אישית של הנגן
כמה דוגמאות נפוצות של התאמה אישית של הנגן על ידי החדרת רכיבים: שמתואר בהמשך.
הגדרת ערימת הרשת
יש לנו דף בנושא התאמה אישית של מקבץ הרשת שמשמש את ExoPlayer.
שמירת נתונים שנטענים מהרשת במטמון
יש לעיין במדריכים עבור שמירה זמנית במטמון תוך כדי תנועה והורדת מדיה.
התאמה אישית של אינטראקציות עם השרת
יכול להיות שחלק מהאפליקציות ירצו ליירט בקשות ותגובות של HTTP. מומלץ: להחדיר כותרות בקשה מותאמות אישית, לקרוא את כותרות התגובה של השרת, לשנות את בקשות מזהי URI וכו'. לדוגמה, האפליקציה שלך עשויה לאמת את עצמה על ידי החדרה אסימון ככותרת כשמבקשים את קטעי המדיה.
הדוגמה הבאה ממחישה איך ליישם את ההתנהגויות האלה
החדרה DataSource.Factory
בהתאמה אישית אל DefaultMediaSourceFactory
:
Kotlin
val dataSourceFactory = DataSource.Factory { val dataSource = httpDataSourceFactory.createDataSource() // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value") dataSource } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory) ) .build()
Java
DataSource.Factory dataSourceFactory = () -> { HttpDataSource dataSource = httpDataSourceFactory.createDataSource(); // Set a custom authentication request header. dataSource.setRequestProperty("Header", "Value"); return dataSource; }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(dataSourceFactory)) .build();
בקטע הקוד שלמעלה, השדה HttpDataSource
שהוחדר כולל את הכותרת
"Header: Value"
בכל בקשת HTTP. ההתנהגות הזו קבועה בכל
עם מקור HTTP.
לגישה מפורטת יותר, אפשר להחדיר התנהגות 'בדיוק בזמן' באמצעות
ResolvingDataSource
קטע הקוד הבא מראה איך לבצע החדרה
כותרות בקשות ממש לפני אינטראקציה עם מקור HTTP:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time request headers. dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time request headers. dataSpec -> dataSpec.withRequestHeaders(getCustomHeaders(dataSpec.uri)));
אפשר גם להשתמש ב-ResolvingDataSource
כדי לבצע את הפעולות הבאות:
שינויים ב-URI בדיוק בזמן, כפי שמוצג בקטע הקוד הבא:
Kotlin
val dataSourceFactory: DataSource.Factory = ResolvingDataSource.Factory(httpDataSourceFactory) { dataSpec: DataSpec -> // Provide just-in-time URI resolution logic. dataSpec.withUri(resolveUri(dataSpec.uri)) }
Java
DataSource.Factory dataSourceFactory = new ResolvingDataSource.Factory( httpDataSourceFactory, // Provide just-in-time URI resolution logic. dataSpec -> dataSpec.withUri(resolveUri(dataSpec.uri)));
התאמה אישית של הטיפול בשגיאות
הטמעה של LoadErrorHandlingPolicy
בהתאמה אישית מאפשרת לאפליקציות להתאים אישית
האופן שבו ExoPlayer מגיב לשגיאות טעינה. לדוגמה, יכול להיות שאפליקציה מסוימת רוצה להיכשל במהירות
במקום לנסות שוב הרבה פעמים, או להתאים אישית את לוגיקת ההשהיה לפני ניסיון חוזר (backoff)
המדיניות הזו קובעת כמה זמן השחקן ימתין בין כל ניסיון חוזר. קטע הקוד הבא
מראים איך להטמיע לוגיקה מותאמת אישית של השהיה לפני ניסיון חוזר (backoff):
Kotlin
val loadErrorHandlingPolicy: LoadErrorHandlingPolicy = object : DefaultLoadErrorHandlingPolicy() { override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorInfo): Long { // Implement custom back-off logic here. return 0 } } val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setLoadErrorHandlingPolicy(loadErrorHandlingPolicy) ) .build()
Java
LoadErrorHandlingPolicy loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy() { @Override public long getRetryDelayMsFor(LoadErrorInfo loadErrorInfo) { // Implement custom back-off logic here. return 0; } }; ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context) .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy)) .build();
הארגומנט LoadErrorInfo
מכיל מידע נוסף על הטעינה שנכשלה
להתאים אישית את הלוגיקה על סמך סוג השגיאה או הבקשה שנכשלה.
התאמה אישית של דגלי כלי החילוץ
ניתן להשתמש בדגלי חילוץ כדי להתאים אישית את אופן החילוץ של פורמטים ספציפיים
ממדיה מתקדמת. אפשר להגדיר אותן בDefaultExtractorsFactory
,
סופקו ל-DefaultMediaSourceFactory
. בדוגמה הבאה מעבירים דגל
שמאפשר ביצוע חיפוש מבוסס אינדקס עבור שידורי MP3.
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING) val player = ExoPlayer.Builder(context) .setMediaSourceFactory(DefaultMediaSourceFactory(context, extractorsFactory)) .build()
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setMp3ExtractorFlags(Mp3Extractor.FLAG_ENABLE_INDEX_SEEKING); ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory(new DefaultMediaSourceFactory(context, extractorsFactory)) .build();
הפעלת דילוג על קצב העברת נתונים קבוע
בסטרימינג של MP3, ADTS ו-AMR, ניתן להפעיל דילוג משוער באמצעות
הנחה של קצב העברת נתונים קבוע עם דגלים של FLAG_ENABLE_CONSTANT_BITRATE_SEEKING
.
אפשר להגדיר את הדגלים האלה לכלי חילוץ ספציפיים באמצעות המאפיין
DefaultExtractorsFactory.setXyzExtractorFlags
methods כמו שמתואר למעלה. שפת תרגום
לאפשר חיפוש בקצב העברת נתונים קבוע לכל המחלצים שתומכים בכך,
DefaultExtractorsFactory.setConstantBitrateSeekingEnabled
Kotlin
val extractorsFactory = DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true)
Java
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory().setConstantBitrateSeekingEnabled(true);
לאחר מכן ניתן להזריק את ExtractorsFactory
דרך DefaultMediaSourceFactory
בתור
שמתואר למעלה להתאמה אישית של דגלי כלי החילוץ.
הפעלת תור של מאגר נתונים זמני אסינכרוני
תורים במאגרי נתונים זמניים אסינכרוניים הם שיפור בעיבוד של ExoPlayer
צינור עיבוד נתונים, שמפעיל MediaCodec
מכונות במצב אסינכרוני
משתמשת בשרשורים נוספים כדי לתזמן פענוח ורינדור של נתונים. מפעיל אותו
יכול להפחית את השמטה של הפריימים והקטעים של האודיו.
יצירת תורים במאגר הנתונים הזמני האסינכרוניים מופעלת כברירת מחדל במכשירים עם Android 12. (רמת API 31) ומעלה, וניתן להפעיל אותה באופן ידני החל מ-Android 6.0 (רמת API 23). כדאי להפעיל את התכונה במכשירים ספציפיים שבהם תזוהה ירידה פריימים או אודיו חסרים, במיוחד בזמן הפעלה שמוגנת באמצעות ניהול זכויות דיגיטליות (DRM) או בקצב פריימים גבוה תוכן.
במקרה הפשוט ביותר, צריך להחדיר DefaultRenderersFactory
אל
הנגן הבא:
Kotlin
val renderersFactory = DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing() val exoPlayer = ExoPlayer.Builder(context, renderersFactory).build()
Java
DefaultRenderersFactory renderersFactory = new DefaultRenderersFactory(context).forceEnableMediaCodecAsynchronousQueueing(); ExoPlayer exoPlayer = new ExoPlayer.Builder(context, renderersFactory).build();
אם אתם יוצרים רינדור ישירות, מעבירים
AsynchronousMediaCodecAdapter.Factory
אל MediaCodecVideoRenderer
ו
MediaCodecAudioRenderer
בנאים.
הפרדת קריאות בשיטת יירוט באמצעות ForwardingPlayer
אפשר להתאים אישית חלק מההתנהגות של מופע Player
בתוך
מחלקה משנית של ForwardingPlayer
ושיטות החלפה כדי לבצע אחת מהפעולות הבאות
הבאים:
- פרמטרים של גישה לפני העברתם למקבל הגישה
Player
. - גישה לערך המוחזר מ-
Player
בעל הגישה לפני החזרתו. - מטמיעים מחדש את השיטה.
כשמבטלים ForwardingPlayer
שיטות, חשוב לוודא
בהטמעה צריכה לפעול על בסיס עקבי ולעמוד בדרישות של Player
הממשק, במיוחד כאשר עוסקים בשיטות שמיועדות ליצור
התנהגות זהה או קשורה. לדוגמה:
- אם רוצים לשנות כל 'הפעלה' צריך לבטל את שניהם
ForwardingPlayer.play
ו-ForwardingPlayer.setPlayWhenReady
, כי יצפה שהתנהגות שיטות אלה תהיה זהה כאשרplayWhenReady = true
. - אם רוצים לשנות את מרווח ההרצה קדימה, צריך לבטל את שניהם.
ForwardingPlayer.seekForward
כדי לבצע דילוג עם טקסט מותאם אישית וגםForwardingPlayer.getSeekForwardIncrement
כדי לדווח את המרווח הנכון בהתאמה אישית חזרה למתקשר. - כדי לקבוע איזה תוכן שחקן מפרסם
Player.Commands
צריך לשנות גם אתPlayer.getAvailableCommands()
וגםPlayer.isCommandAvailable()
, וגם להאזין התקשרות חזרהPlayer.Listener.onAvailableCommandsChanged()
לקבלת הודעה על שינויים שמגיעים מהנגן המקורי.
התאמה אישית של MediaSource
הדוגמאות שלמעלה מחדירות רכיבים מותאמים אישית לשימוש במהלך ההפעלה של כל הפריטים
MediaItem
אובייקטים שמועברים לנגן. כאשר התאמה אישית פרטנית
נדרשת גם אפשרות להחדיר רכיבים מותאמים אישית
MediaSource
מכונות, שניתן להעביר ישירות לנגן. הדוגמה
למטה רואים איך להתאים אישית ProgressiveMediaSource
כדי להשתמש
DataSource.Factory
, ExtractorsFactory
וגם LoadErrorHandlingPolicy
:
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri))
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(customDataSourceFactory, customExtractorsFactory) .setLoadErrorHandlingPolicy(customLoadErrorHandlingPolicy) .createMediaSource(MediaItem.fromUri(streamUri));
יצירת רכיבים מותאמים אישית
הספרייה מספקת יישומי ברירת מחדל של הרכיבים שמופיעים למעלה
בדף הזה במקרים נפוצים. ExoPlayer
יכול להשתמש ברכיבים האלה, אבל
יכול לשמש גם בהטמעות מותאמות אישית אם התנהגויות לא סטנדרטיות
נדרש. לפניכם מספר תרחישים לדוגמה של הטמעות בהתאמה אישית:
Renderer
– כדאי להטמיעRenderer
בהתאמה אישית כדי לטפל סוג המדיה הזה אינו נתמך על ידי יישומי ברירת המחדל שמסופקים על ידי לספרייה.TrackSelector
– הטמעה שלTrackSelector
בהתאמה אישית מאפשרת לאפליקציה כדי לשנות את האופן שבו מסלולים שנחשפים על ידיMediaSource
נבחר לצריכה על ידי כל אחד מהRenderer
הזמינים.LoadControl
– הטמעה שלLoadControl
בהתאמה אישית מאפשרת לאפליקציה כדי לשנות את מדיניות האחסון הזמני של הנגן.Extractor
– אם צריך לתמוך בפורמט של קונטיינר שלא נתמך כרגע שנתמכת על ידי הספרייה, כדאי להטמיע מחלקה מותאמת אישית שלExtractor
.MediaSource
– ההטמעה של מחלקה מותאמת אישית שלMediaSource
עשויה להיות מתאים אם רוצים לקבל דוגמיות מדיה לפיד לרינדור או אם רוצים להטמיע הרכבה מותאמת אישית שלMediaSource
או התנהגות המשתמשים.MediaSource.Factory
– הטמעה שלMediaSource.Factory
בהתאמה אישית מאפשרת לאפליקציה להתאים אישית את הדרך שבה נוצרMediaSource
מ-MediaItem
.DataSource
– חבילת ה-upstream של ExoPlayer כבר מכילה מספר הטמעות שלDataSource
לתרחישים שונים לדוגמה. מומלץ: להטמיע את הכיתהDataSource
שבבעלותכם כדי לטעון נתונים בדרך אחרת, למשל מעל פרוטוקול בהתאמה אישית, באמצעות מחסנית HTTP מותאמת אישית או מתוך מאגר תגים בהתאמה אישית של Google.
כשיוצרים רכיבים בהתאמה אישית, מומלץ:
- אם רכיב מותאם אישית צריך לדווח על אירועים חזרה לאפליקציה, מומלץ
לעשות זאת באמצעות אותו הדגם של רכיבי ExoPlayer הקיימים,
לדוגמה, באמצעות
EventDispatcher
מחלקות או העברתHandler
יחד עם את ה-constructor של הרכיב. - אנחנו ממליצים ברכיבים מותאמים אישית להשתמש באותו דגם כמו ExoPlayer הקיים
כדי לאפשר לאפליקציה לקבוע הגדרות מחדש במהלך ההפעלה. כדי לעשות את זה,
רכיבים מותאמים אישית צריכים להטמיע את
PlayerMessage.Target
ולקבל שינויים בתצורה בשיטהhandleMessage
. קוד האפליקציה צריך מעבירים את השינויים בהגדרות על ידי קריאה ל-method שלcreateMessage
של ExoPlayer, להגדרת ההודעה ושליחתה לרכיב באמצעותPlayerMessage.send
. שליחת הודעות שמיועדות לשרשור של ההפעלה מבטיחה שהן יבוצעו לפי הסדר שבו כל פעולה אחרת שמבוצע בנגן.