گوش دادن به رویدادهای پخش
رویدادهایی مانند تغییرات در وضعیت و خطاهای پخش، به نمونههای ثبتشدهی Player.Listener گزارش میشوند. برای ثبت یک شنونده برای دریافت چنین رویدادهایی:
کاتلین
// Add a listener to receive events from the player. player.addListener(listener)
جاوا
// Add a listener to receive events from the player. player.addListener(listener);
Player.Listener متدهای پیشفرض خالی دارد، بنابراین شما فقط باید متدهایی را که به آنها علاقه دارید پیادهسازی کنید. برای توضیحات کامل متدها و زمان فراخوانی آنها به Javadoc مراجعه کنید. برخی از مهمترین متدها با جزئیات بیشتر در زیر توضیح داده شدهاند.
شنوندگان میتوانند بین پیادهسازی فراخوانیهای رویداد منفرد یا فراخوانی عمومی onEvents که پس از وقوع همزمان یک یا چند رویداد فراخوانی میشود، یکی را انتخاب کنند. برای توضیح اینکه کدام یک باید برای موارد استفاده مختلف ترجیح داده شود، به Individual callbacks vs onEvents مراجعه کنید.
تغییرات وضعیت پخش
تغییرات در وضعیت بازیکن را میتوان با پیادهسازی onPlaybackStateChanged(@State int state) در یک Player.Listener ثبتشده دریافت کرد. بازیکن میتواند در یکی از چهار وضعیت پخش زیر باشد:
-
Player.STATE_IDLE: این وضعیت اولیه، وضعیتی که پخشکننده متوقف میشود و وضعیتی که پخش ناموفق است، میباشد. پخشکننده در این وضعیت فقط منابع محدودی را در اختیار خواهد داشت. -
Player.STATE_BUFFERING: بازیکن نمیتواند بلافاصله از موقعیت فعلی خود بازی کند. این اتفاق عمدتاً به این دلیل میافتد که دادههای بیشتری باید بارگذاری شوند. -
Player.STATE_READY: بازیکن میتواند بلافاصله از موقعیت فعلی خود بازی کند. -
Player.STATE_ENDED: پخشکننده تمام رسانهها را به پایان رساند.
علاوه بر این حالتها، بازیکن یک پرچم playWhenReady دارد که قصد کاربر برای بازی را نشان میدهد. تغییرات در این پرچم را میتوان با پیادهسازی onPlayWhenReadyChanged(playWhenReady, @PlayWhenReadyChangeReason int reason) دریافت کرد.
یک بازیکن زمانی در حال بازی است (یعنی موقعیتش در حال پیشروی است و رسانه به کاربر ارائه میشود) که هر سه شرط زیر برقرار باشد:
- بازیکن در وضعیت
Player.STATE_READYقرار دارد. -
playWhenReadytrueاست - پخش به دلیلی که توسط
Player.getPlaybackSuppressionReasonبرگردانده شده است، متوقف نمیشود.
به جای اینکه مجبور باشیم این ویژگیها را به صورت جداگانه بررسی کنیم، میتوانیم Player.isPlaying را فراخوانی کنیم. تغییرات در این حالت را میتوان با پیادهسازی onIsPlayingChanged(boolean isPlaying) دریافت کرد:
کاتلین
player.addListener( object : Player.Listener { override fun onIsPlayingChanged(isPlaying: Boolean) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.playWhenReady, // player.playbackState, player.playbackSuppressionReason and // player.playerError for details. } } } )
جاوا
player.addListener( new Player.Listener() { @Override public void onIsPlayingChanged(boolean isPlaying) { if (isPlaying) { // Active playback. } else { // Not playing because playback is paused, ended, suppressed, or the player // is buffering, stopped or failed. Check player.getPlayWhenReady, // player.getPlaybackState, player.getPlaybackSuppressionReason and // player.getPlaybackError for details. } } });
خطاهای پخش
خطاهایی که باعث عدم موفقیت در پخش میشوند را میتوان با پیادهسازی onPlayerError(PlaybackException error) در یک Player.Listener ثبتشده دریافت کرد. هنگامی که یک خرابی رخ میدهد، این متد بلافاصله قبل از انتقال وضعیت پخش به Player.STATE_IDLE فراخوانی میشود. پخشهای ناموفق یا متوقفشده را میتوان با فراخوانی ExoPlayer.prepare دوباره امتحان کرد.
توجه داشته باشید که برخی از پیادهسازیهای Player نمونههایی از زیرکلاسهای PlaybackException را برای ارائه اطلاعات بیشتر در مورد خرابی ارسال میکنند. برای مثال، ExoPlayer ، ExoPlaybackException را ارسال میکند که دارای type ، rendererIndex و سایر فیلدهای مختص ExoPlayer است.
مثال زیر نحوه تشخیص عدم موفقیت پخش به دلیل مشکل شبکه HTTP را نشان میدهد:
کاتلین
player.addListener( object : Player.Listener { override fun onPlayerError(error: PlaybackException) { val cause = error.cause if (cause is HttpDataSourceException) { // An HTTP error occurred. val httpError = cause // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError is InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } } )
جاوا
player.addListener( new Player.Listener() { @Override public void onPlayerError(PlaybackException error) { @Nullable Throwable cause = error.getCause(); if (cause instanceof HttpDataSourceException) { // An HTTP error occurred. HttpDataSourceException httpError = (HttpDataSourceException) cause; // It's possible to find out more about the error both by casting and by querying // the cause. if (httpError instanceof HttpDataSource.InvalidResponseCodeException) { // Cast to InvalidResponseCodeException and retrieve the response code, message // and headers. } else { // Try calling httpError.getCause() to retrieve the underlying cause, although // note that it may be null. } } } });
انتقال لیست پخش
هر زمان که پخشکننده به یک آیتم رسانهای جدید در لیست پخش تغییر میکند onMediaItemTransition(MediaItem mediaItem, @MediaItemTransitionReason int reason) در اشیاء ثبتشده Player.Listener فراخوانی میشود. دلیل نشان میدهد که آیا این یک انتقال خودکار، یک جستجو (برای مثال پس از فراخوانی player.next() )، تکرار همان آیتم یا ناشی از تغییر لیست پخش (برای مثال، اگر آیتم در حال پخش حذف شود) بوده است.
فراداده
متادیتای برگردانده شده از player.getCurrentMediaMetadata() میتواند به دلایل زیادی تغییر کند: انتقال لیست پخش، بهروزرسانیهای متادیتای درون پخش یا بهروزرسانی MediaItem فعلی در اواسط پخش.
اگر به تغییرات متادیتا علاقهمند هستید، مثلاً برای بهروزرسانی رابط کاربری که عنوان فعلی را نشان میدهد، میتوانید به onMediaMetadataChanged گوش دهید.
جستجو
فراخوانی متدهای Player.seekTo منجر به یک سری فراخوانیهای مجدد به نمونههای ثبتشدهی Player.Listener میشود:
-
onPositionDiscontinuityباreason=DISCONTINUITY_REASON_SEEK. این نتیجه مستقیم فراخوانیPlayer.seekToاست. این فراخوانی دارای فیلدهایPositionInfoبرای موقعیت قبل و بعد از جستجو است. -
onPlaybackStateChangedبا هرگونه تغییر وضعیت فوری مربوط به جستجو. توجه داشته باشید که ممکن است چنین تغییری وجود نداشته باشد.
فراخوانیهای تکی در مقابل onEvents
شنوندگان میتوانند بین پیادهسازی فراخوانیهای تکی مانند onIsPlayingChanged(boolean isPlaying) و فراخوانی ژنریک onEvents(Player player, Events events) را انتخاب کنند. فراخوانی ژنریک دسترسی به شیء Player را فراهم میکند و مجموعهای از events را که با هم رخ دادهاند مشخص میکند. این فراخوانی همیشه پس از فراخوانیهایی که مربوط به رویدادهای تکی هستند، فراخوانی میشود.
کاتلین
override fun onEvents(player: Player, events: Player.Events) { if ( events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED) ) { uiModule.updateUi(player) } }
جاوا
@Override public void onEvents(Player player, Events events) { if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED) || events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) { uiModule.updateUi(player); } }
در موارد زیر، رویدادهای انفرادی باید ترجیح داده شوند:
- شنونده به دلایل تغییرات علاقهمند است. برای مثال، دلایل ارائه شده برای
onPlayWhenReadyChangedیاonMediaItemTransition. - شنونده فقط بر اساس مقادیر جدید ارائه شده از طریق پارامترهای callback عمل میکند یا چیز دیگری را که به پارامترهای callback بستگی ندارد، فعال میکند.
- پیادهسازی شنونده ترجیح میدهد که در نام متد، یک نشانه خوانا و واضح از آنچه باعث وقوع رویداد شده است، وجود داشته باشد.
- شنونده به یک سیستم تحلیلی گزارش میدهد که باید از تمام رویدادهای منفرد و تغییرات وضعیت مطلع باشد.
در موارد زیر، نوع ژنریک onEvents(Player player, Events events) باید ترجیح داده شود:
- شنونده میخواهد منطق یکسانی را برای چندین رویداد فعال کند. برای مثال، بهروزرسانی یک رابط کاربری برای هر دو رویداد
onPlaybackStateChangedوonPlayWhenReadyChanged. - شنونده برای ایجاد رویدادهای بیشتر، مثلاً جستجوی انتقال یک آیتم رسانهای، باید به شیء
Playerدسترسی داشته باشد. - شنونده قصد دارد از چندین مقدار حالت که از طریق فراخوانیهای جداگانه با هم یا در ترکیب با متدهای دریافتکنندهی
Playerگزارش میشوند، استفاده کند. برای مثال، استفاده ازPlayer.getCurrentWindowIndex()باTimelineارائه شده درonTimelineChangedفقط از داخل فراخوانیonEventsایمن است. - شنونده علاقهمند است بداند که آیا رویدادها به طور منطقی با هم رخ دادهاند یا خیر. برای مثال، به دلیل تغییر یک آیتم رسانهای
onPlaybackStateChangedبهSTATE_BUFFERINGتغییر یافته است.
در برخی موارد، ممکن است شنوندهها نیاز داشته باشند که فراخوانیهای منفرد را با فراخوانی عمومی onEvents ترکیب کنند، برای مثال برای ثبت دلایل تغییر آیتم رسانه با onMediaItemTransition ، اما فقط زمانی عمل میکنند که همه تغییرات حالت بتوانند با هم در onEvents استفاده شوند.
استفاده از AnalyticsListener
هنگام استفاده از ExoPlayer ، میتوان با فراخوانی addAnalyticsListener ، یک AnalyticsListener را در پخشکننده ثبت کرد. پیادهسازیهای AnalyticsListener قادر به گوش دادن به رویدادهای دقیقی هستند که ممکن است برای اهداف تحلیلی و ثبت وقایع مفید باشند. لطفاً برای جزئیات بیشتر به صفحه تحلیلی مراجعه کنید.
استفاده از EventLogger
EventLogger یک AnalyticsListener است که مستقیماً توسط کتابخانه برای اهداف ثبت وقایع ارائه میشود. EventLogger به ExoPlayer اضافه کنید تا ثبت وقایع اضافی مفید را با یک خط کد فعال کنید:
کاتلین
player.addAnalyticsListener(EventLogger())
جاوا
player.addAnalyticsListener(new EventLogger());
برای جزئیات بیشتر به صفحه ثبت اشکالزدایی مراجعه کنید.
اجرای رویدادها در موقعیتهای پخش مشخص شده
Some use cases require firing events at specified playback positions. This is supported using PlayerMessage . A PlayerMessage can be created using ExoPlayer.createMessage . The playback position at which it should be executed can be set using PlayerMessage.setPosition . Messages are executed on the playback thread by default, but this can be customized using PlayerMessage.setLooper . PlayerMessage.setDeleteAfterDelivery can be used to control whether the message will be executed every time the specified playback position is encountered (this may happen multiple times due to seeking and repeat modes), or just the first time. Once the PlayerMessage is configured, it can be scheduled using PlayerMessage.send .
کاتلین
player .createMessage { messageType: Int, payload: Any? -> } .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send()
جاوا
player .createMessage( (messageType, payload) -> { // Do something at the specified playback position. }) .setLooper(Looper.getMainLooper()) .setPosition(/* mediaItemIndex= */ 0, /* positionMs= */ 120_000) .setPayload(customPayloadData) .setDeleteAfterDelivery(false) .send();