تغییرات در وضعیت «پخشکننده» (مانند شروع بازپخش، میانگیری، یا خطاها)
رویدادهایی را راهاندازی میکند که به نمونههای ثبتشده Player.Listener ارسال میشوند. این
رویدادها با ثابتهای عدد صحیح نشان داده میشوند و با Player.Event
و Player.Events تعریف میشوند.
ثبت Player.Listener
رویدادهای بازیکن به نمونههای ثبتشده 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);
اگر از Kotlin استفاده میکنید، میتوانید از توابع افزونه تعلیق
ارائهشده توسط ماژول media3-common-ktx نیز برای گوش دادن به رویدادها بااستفاده از کوروتینها استفاده کنید.
در این صورت، نیازی به ثبت یا لغو ثبت صریح Player.Listener نخواهید داشت.
گوش دادن به رویدادهای بازپخش بااستفاده از Player.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بهدلیل انتقال فایل رسانهای.
In some cases, listeners may need to combine the individual callbacks with the
generic onEvents callback, for example to record media item change reasons
with onMediaItemTransition, but only act once all state changes can be used
together in onEvents.
گوش دادن به رویدادهای بازپخش بااستفاده از روالهای همگام
یا میتوانید روال مشترک Kotlin را بااستفاده از Player.listenTo راهاندازی کنید و
Player.Event مربوطه را مشخص کنید:
توجه داشته باشید که Player.listen و Player.listenTo را میتوان از هر رشتهای فراخوانی کرد، درحالیکه لامبدا فراخوان برگشتی همیشه در رشته مرتبط با Player.getApplicationLooper فراخوانی میشود. Therefore, it is safe to access Player
methods and state properties inside the callback lambda even if the coroutine
was launched on a different thread.
تغییرات وضعیت بازپخش
coroutineScope.launch { player.listenTo(Player.EVENT_IS_PLAYING_CHANGED) { // `Player` is a receiver scope for this trailing lambda if (isPlaying) { // Active playback. } else { // Not playing. } } }
خطاهای بازپخش
coroutineScope.launch { player.listenTo(Player.EVENT_PLAYER_ERROR) { val error = playerError ?: return@listenTo val cause = error.cause if (cause is HttpDataSourceException) { // An HTTP error occurred. if (cause is InvalidResponseCodeException) { // Retrieve the response code, message and headers } else { // Try calling cause.cause to retrieve the underlying cause } } } }
تماسهای برگشتی فردی درمقابل onEvents
هنگام گوش دادن به رویدادهای بازیکن در داخل یک کوروتین، همیشه پیادهسازی را برای callback onEvents ارائه خواهید داد، برخلاف یک callback جداگانه. میتوانید بین
Player.listen و Player.listenTo انتخاب کنید، بسته به اینکه کدام رویدادها باید
فراخوانی لامبدا را راهاندازی کنند. اما توابع در موارد دیگر
معادل هستند:
گوش دادن
coroutineScope.launch { player.listen { events -> // `Player` is a receiver scope for this trailing lambda if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) { // Access the player state directly from the receiver updateUi(playbackState) } if (events.contains(Player.EVENT_PLAYER_ERROR)) { // Access the error directly from the player handleError(playerError) } } }
listenTo
coroutineScope.launch { player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events -> // `Player` is a receiver scope for this trailing lambda if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) { // Access the player state directly from the receiver updateUi(playbackState) } if (events.contains(Player.EVENT_PLAYER_ERROR)) { // Access the error directly from the player handleError(playerError) } } }
اگر به چند نوع رویداد علاقهمندید، میتوانید فهرستی از رویدادها را به
Player.listenTo ارسال کنید. هرزمان که هریک از آن رویدادها رخ دهد، لامبدا شما فراخوانی میشود و میتوانید پارامتر Events را بررسی کنید تا ببینید کدام رویدادها واقعاً فعال شدهاند:
coroutineScope.launch { player.listenTo(Player.EVENT_PLAYBACK_STATE_CHANGED, Player.EVENT_PLAYER_ERROR) { events -> // Unclear which event got triggered without querying `events` parameter // The following function will fire whenever either one is caught updateUiAndHandleError(playbackState, playerError) } }
چون این توابع روی onEvents عمل میکنند، دسترسی به آرگومانهای گذرا را که به بازخوانهای فردی ارسال میشوند فراهم نمیکنند، مانند دلیل در onMediaItemTransition(..., int reason) یا oldPosition در onPositionDiscontinuity(...). اگر منطق شما به این آرگومانهای خاص متکی است
(و این آرگومانها بهعنوان ویژگیهای وضعیت در Player دردسترس نیستند)، باید
بهجای آن از میانای استاندارد Player.Listener استفاده کنید.
استفاده از AnalyticsListener
هنگام استفاده از ExoPlayer، میتوان با فراخوانی addAnalyticsListener، یک AnalyticsListener را در پخشکننده ثبت کرد. پیادهسازیهای AnalyticsListener میتوانند
رویدادهای دقیق را که ممکن است برای اهداف تجزیهوتحلیل و ثبت مفید باشند
بشنوند. برای جزئیات بیشتر، به صفحه Analytics مراجعه کنید.
استفاده از EventLogger
EventLogger یک AnalyticsListener است که مستقیماً توسط کتابخانه برای اهداف ثبت گزارش ارائه شده است. EventLogger را به ExoPlayer اضافه کنید تا ثبت گزارش مفید
اضافی با یک خط فعال شود:
کاتلین
player.addAnalyticsListener(EventLogger())
جاوا
player.addAnalyticsListener(new EventLogger());
برای جزئیات بیشتر، صفحه ثبت گزارش اشکالزدایی را ببینید.
رویدادهای Fire در موقعیتهای بازپخش مشخصشده
برخیاز موارد استفاده نیاز به راهاندازی رویدادها در موقعیتهای بازپخش مشخصشده دارند. این مورد بااستفاده از PlayerMessage پشتیبانی میشود. PlayerMessage را میتوان بااستفاده از
ExoPlayer.createMessage ایجاد کرد. موقعیت بازپخش که باید در آن اجرا شود
میتواند بااستفاده از PlayerMessage.setPosition تنظیم شود. پیامها بهطور پیشفرض در رشته بازپخش اجرا میشوند، اما این رفتار را میتوان بااستفاده از PlayerMessage.setLooper سفارشیسازی کرد. از PlayerMessage.setDeleteAfterDelivery میتوان برای کنترل اینکه آیا پیام هر بار که به موقعیت بازپخش مشخصشده میرسد اجرا شود یا نه (این اتفاق ممکن است چندین بار بهدلیل جستجو و حالتهای تکرار رخ دهد) یا فقط بار اول استفاده کرد. وقتی PlayerMessage پیکربندی شد،
میتوان آن را بااستفاده از 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();