با استفاده از MediaSession پخش را کنترل و تبلیغ کنید

جلسات رسانه‌ای یک روش جهانی برای تعامل با یک پخش‌کننده صوتی یا تصویری ارائه می‌دهند. در Media3، پخش‌کننده پیش‌فرض کلاس ExoPlayer است که رابط Player را پیاده‌سازی می‌کند. اتصال جلسه رسانه‌ای به پخش‌کننده به یک برنامه اجازه می‌دهد تا پخش رسانه را به صورت خارجی اعلام کند و دستورات پخش را از منابع خارجی دریافت کند.

دستورات ممکن است از دکمه‌های فیزیکی مانند دکمه پخش روی هدست یا کنترل از راه دور تلویزیون سرچشمه بگیرند. همچنین ممکن است از برنامه‌های کلاینت که دارای کنترل‌کننده رسانه هستند، مانند دستور "مکث" به دستیار گوگل، ناشی شوند. جلسه رسانه این دستورات را به پخش‌کننده برنامه رسانه واگذار می‌کند.

چه زمانی یک جلسه رسانه‌ای را انتخاب کنیم؟

وقتی MediaSession پیاده‌سازی می‌کنید، به کاربران اجازه می‌دهید پخش را کنترل کنند:

  • از طریق هدفون‌هایشان . اغلب دکمه‌ها یا تعاملات لمسی وجود دارد که کاربر می‌تواند روی هدفون خود برای پخش یا مکث رسانه یا رفتن به آهنگ بعدی یا قبلی انجام دهد.
  • با صحبت کردن با دستیار گوگل . یک الگوی رایج این است که بگویید «OK Google, pause» تا هر رسانه‌ای که در حال پخش در دستگاه است، متوقف شود.
  • از طریق ساعت Wear OS آنها. این امکان دسترسی آسان‌تر به رایج‌ترین کنترل‌های پخش را هنگام پخش در تلفنشان فراهم می‌کند.
  • از طریق کنترل‌های رسانه . این چرخ فلک کنترل‌های مربوط به هر جلسه رسانه‌ای در حال اجرا را نشان می‌دهد.
  • روی تلویزیون . امکان انجام عملیات با دکمه‌های پخش فیزیکی، کنترل پخش پلتفرم و مدیریت توان را فراهم می‌کند (برای مثال، اگر تلویزیون، ساندبار یا گیرنده A/V خاموش شود یا ورودی تغییر کند، پخش باید در برنامه متوقف شود).
  • از طریق کنترل‌های رسانه‌ای اندروید اتو . این امکان کنترل ایمن پخش را هنگام رانندگی فراهم می‌کند.
  • و هر فرآیند خارجی دیگری که باید بر پخش تأثیر بگذارد.

این برای بسیاری از موارد استفاده عالی است. به طور خاص، شما باید استفاده از MediaSession را در موارد زیر به شدت در نظر بگیرید:

  • شما در حال پخش محتوای ویدیویی طولانی مانند فیلم یا تلویزیون زنده هستید.
  • شما در حال پخش محتوای صوتی طولانی مانند پادکست‌ها یا لیست‌های پخش موسیقی هستید.
  • شما در حال ساخت یک اپلیکیشن تلویزیونی هستید.

با این حال، همه موارد استفاده با MediaSession مطابقت ندارند. ممکن است بخواهید فقط در موارد زیر از Player استفاده کنید:

  • شما در حال نمایش محتوای کوتاه هستید، که در آن به هیچ کنترل خارجی یا پخش پس‌زمینه‌ای نیاز نیست.
  • حتی یک ویدیوی فعال هم وجود ندارد، مثلاً کاربر در حال پیمایش یک لیست است و چندین ویدیو همزمان روی صفحه نمایش داده می‌شوند .
  • شما در حال پخش یک ویدیوی معرفی یا توضیحی تک قسمتی هستید که انتظار دارید کاربر بدون نیاز به کنترل‌های پخش خارجی، آن را تماشا کند.
  • محتوای شما حساس به حریم خصوصی است و نمی‌خواهید فرآیندهای خارجی به فراداده‌های رسانه‌ای دسترسی داشته باشند (برای مثال حالت ناشناس در مرورگر).

اگر مورد استفاده شما با هیچ یک از موارد ذکر شده در بالا مطابقت ندارد، در نظر بگیرید که آیا با ادامه پخش برنامه خود در زمانی که کاربر به طور فعال با محتوا درگیر نیست، مشکلی ندارید. اگر پاسخ مثبت است، احتمالاً می‌خواهید MediaSession انتخاب کنید. اگر پاسخ منفی است، احتمالاً می‌خواهید از Player به جای آن استفاده کنید.

ایجاد یک جلسه رسانه‌ای

یک جلسه رسانه‌ای در کنار پخش‌کننده‌ای که مدیریت می‌کند، وجود دارد. شما می‌توانید یک جلسه رسانه‌ای را با یک Context و یک شیء Player بسازید. شما باید یک جلسه رسانه‌ای را در صورت نیاز ایجاد و مقداردهی اولیه کنید، مانند متد چرخه عمر onStart() یا onResume() از Activity یا Fragment ، یا متد onCreate() از Service که مالک جلسه رسانه‌ای و پخش‌کننده مرتبط با آن است.

برای ایجاد یک جلسه رسانه‌ای، یک Player مقداردهی اولیه کنید و آن را به MediaSession.Builder مانند زیر ارائه دهید:

کاتلین

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()

جاوا

ExoPlayer player = new ExoPlayer.Builder(context).build();
MediaSession mediaSession = new MediaSession.Builder(context, player).build();

مدیریت خودکار وضعیت

کتابخانه Media3 به طور خودکار جلسه رسانه را با استفاده از وضعیت پخش‌کننده به‌روزرسانی می‌کند. به این ترتیب، نیازی نیست که نگاشت را از پخش‌کننده به جلسه به صورت دستی مدیریت کنید.

این با جلسه رسانه پلتفرم متفاوت است که در آن شما نیاز داشتید یک PlaybackState مستقل از خود پخش‌کننده ایجاد و نگهداری کنید، برای مثال برای نشان دادن هرگونه خطا .

شناسه جلسه منحصر به فرد

به طور پیش‌فرض، MediaSession.Builder یک session با یک رشته خالی به عنوان شناسه session ایجاد می‌کند. این مقدار در صورتی کافی است که یک برنامه قصد داشته باشد فقط یک نمونه session ایجاد کند، که رایج‌ترین حالت است.

اگر یک برنامه بخواهد چندین نمونه جلسه را همزمان مدیریت کند، برنامه باید اطمینان حاصل کند که شناسه جلسه هر جلسه منحصر به فرد است. شناسه جلسه را می‌توان هنگام ساخت جلسه با MediaSession.Builder.setId(String id) تنظیم کرد.

اگر IllegalStateException مواجه شدید که برنامه شما را با پیام خطای IllegalStateException: Session ID must be unique. ID= از کار می‌اندازد، احتمالاً یک session به طور غیرمنتظره قبل از انتشار نمونه‌ای که قبلاً با همان ID ایجاد شده بود، ایجاد شده است. برای جلوگیری از نشت sessionها توسط یک خطای برنامه‌نویسی، چنین مواردی با ارسال یک exception شناسایی و اطلاع داده می‌شوند.

اعطای کنترل به سایر کلاینت‌ها

جلسه رسانه کلید کنترل پخش است. این به شما امکان می‌دهد دستورات را از منابع خارجی به پخش‌کننده‌ای که کار پخش رسانه شما را انجام می‌دهد، هدایت کنید. این منابع می‌توانند دکمه‌های فیزیکی مانند دکمه پخش روی هدست یا کنترل از راه دور تلویزیون یا دستورات غیرمستقیم مانند دستور "مکث" به دستیار گوگل باشند. به همین ترتیب، ممکن است بخواهید به سیستم اندروید برای تسهیل کنترل اعلان‌ها و قفل صفحه یا به یک ساعت Wear OS دسترسی بدهید تا بتوانید پخش را از صفحه ساعت کنترل کنید. کلاینت‌های خارجی می‌توانند از یک کنترل‌کننده رسانه برای صدور دستورات پخش به برنامه رسانه شما استفاده کنند. این دستورات توسط جلسه رسانه شما دریافت می‌شوند که در نهایت دستورات را به پخش‌کننده رسانه واگذار می‌کند.

نموداری که تعامل بین یک MediaSession و MediaController را نشان می‌دهد.
شکل 1 : کنترل‌کننده رسانه، انتقال دستورات از منابع خارجی به جلسه رسانه را تسهیل می‌کند.

وقتی یک کنترلر می‌خواهد به جلسه رسانه شما متصل شود، متد onConnect() فراخوانی می‌شود. می‌توانید از ControllerInfo ارائه شده برای تصمیم‌گیری در مورد پذیرش یا رد درخواست استفاده کنید. نمونه‌ای از پذیرش درخواست اتصال را در بخش Declare custom commands ببینید.

پس از اتصال، یک کنترل‌کننده می‌تواند دستورات پخش را به جلسه ارسال کند. سپس جلسه این دستورات را به پخش‌کننده واگذار می‌کند. دستورات پخش و لیست پخش تعریف شده در رابط Player به طور خودکار توسط جلسه مدیریت می‌شوند.

سایر متدهای فراخوانی به شما امکان می‌دهند، برای مثال، درخواست‌های مربوط به دستورات سفارشی و تغییر لیست پخش را مدیریت کنید. این فراخوانی‌ها به طور مشابه شامل یک شیء ControllerInfo هستند، بنابراین می‌توانید نحوه پاسخ به هر درخواست را بر اساس هر کنترلر تغییر دهید.

اصلاح لیست پخش

یک جلسه رسانه‌ای می‌تواند مستقیماً لیست پخش پخش‌کننده خود را تغییر دهد، همانطور که در راهنمای لیست‌های پخش ExoPlayer توضیح داده شده است. کنترل‌کننده‌ها همچنین می‌توانند لیست پخش را تغییر دهند اگر COMMAND_SET_MEDIA_ITEM یا COMMAND_CHANGE_MEDIA_ITEMS برای کنترل‌کننده در دسترس باشد.

هنگام اضافه کردن آیتم‌های جدید به لیست پخش، پخش‌کننده معمولاً به نمونه‌هایی MediaItem با یک URI تعریف‌شده نیاز دارد تا آنها را قابل پخش کند. به‌طور پیش‌فرض، آیتم‌های تازه اضافه‌شده اگر URI تعریف‌شده‌ای داشته باشند، به‌طور خودکار به متدهای پخش‌کننده مانند player.addMediaItem ارسال می‌شوند.

اگر می‌خواهید نمونه‌های MediaItem اضافه شده به پخش‌کننده را سفارشی کنید، می‌توانید onAddMediaItems() لغو کنید. این مرحله زمانی لازم است که می‌خواهید از کنترلرهایی که رسانه را بدون URI تعریف شده درخواست می‌کنند، پشتیبانی کنید. در عوض، MediaItem معمولاً یک یا چند فیلد زیر را برای توصیف رسانه درخواستی دارد:

  • MediaItem.id : یک شناسه عمومی که رسانه را شناسایی می‌کند.
  • MediaItem.RequestMetadata.mediaUri : یک URI درخواست که ممکن است از یک طرحواره سفارشی استفاده کند و لزوماً مستقیماً توسط پخش‌کننده قابل پخش نیست.
  • MediaItem.RequestMetadata.searchQuery : یک عبارت جستجوی متنی، برای مثال از دستیار گوگل.
  • MediaItem.MediaMetadata : متادیتای ساختاریافته مانند «عنوان» یا «هنرمند».

برای گزینه‌های سفارشی‌سازی بیشتر برای لیست‌های پخش کاملاً جدید، می‌توانید تابع onSetMediaItems() را که به شما امکان می‌دهد آیتم شروع و موقعیت را در لیست پخش تعریف کنید، لغو کنید. به عنوان مثال، می‌توانید یک آیتم درخواستی واحد را به کل لیست پخش گسترش دهید و به پخش‌کننده دستور دهید که از اندیس آیتم درخواستی اولیه شروع کند. یک پیاده‌سازی نمونه از onSetMediaItems() با این ویژگی را می‌توانید در برنامه آزمایشی جلسه پیدا کنید.

مدیریت تنظیمات دکمه رسانه

هر کنترلر، برای مثال رابط کاربری سیستم، اندروید اتو یا Wear OS، می‌تواند تصمیمات خود را در مورد اینکه کدام دکمه‌ها به کاربر نشان داده شوند، بگیرد. برای مشخص کردن اینکه کدام کنترل‌های پخش را می‌خواهید در معرض دید کاربر قرار دهید، می‌توانید تنظیمات دکمه رسانه را در MediaSession مشخص کنید. این تنظیمات شامل یک لیست مرتب از نمونه‌های CommandButton است که هر کدام یک تنظیمات برای یک دکمه در رابط کاربری تعریف می‌کنند.

تعریف دکمه‌های فرمان

نمونه‌های CommandButton برای تعریف تنظیمات دکمه رسانه استفاده می‌شوند. هر دکمه سه جنبه از عنصر رابط کاربری مورد نظر را تعریف می‌کند:

  1. آیکون ، ظاهر بصری را تعریف می‌کند. هنگام ایجاد یک CommandButton.Builder ، آیکون باید روی یکی از ثابت‌های از پیش تعریف شده تنظیم شود. توجه داشته باشید که این یک منبع واقعی Bitmap یا تصویر نیست. یک ثابت عمومی به کنترلرها کمک می‌کند تا یک منبع مناسب برای ظاهر و حس سازگار در رابط کاربری خود انتخاب کنند. اگر هیچ یک از ثابت‌های آیکون از پیش تعریف شده با مورد استفاده شما مطابقت ندارد، می‌توانید به جای آن از setCustomIconResId استفاده کنید.
  2. Command ، عملی را که هنگام تعامل کاربر با دکمه فعال می‌شود، تعریف می‌کند. می‌توانید setPlayerCommand برای Player.Command یا setSessionCommand برای SessionCommand از پیش تعریف شده یا سفارشی استفاده کنید.
  3. Slot ، محل قرارگیری دکمه در رابط کاربری کنترلر را مشخص می‌کند. این فیلد اختیاری است و به طور خودکار بر اساس Icon و Command تنظیم می‌شود. برای مثال، این امکان را فراهم می‌کند که مشخص کنیم یک دکمه باید در ناحیه ناوبری 'forward' رابط کاربری به جای ناحیه پیش‌فرض 'overflow' نمایش داده شود.

کاتلین

val button =
  CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
    .setSessionCommand(SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

جاوا

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setSessionCommand(new SessionCommand(CUSTOM_ACTION_ID, Bundle.EMPTY))
        .setSlots(CommandButton.SLOT_FORWARD)
        .build();

وقتی تنظیمات دکمه رسانه اعمال شد، الگوریتم زیر اعمال می‌شود:

  1. برای هر CommandButton در تنظیمات دکمه رسانه (media button preferences )، دکمه را در اولین جایگاه موجود و مجاز قرار دهید.
  2. اگر هیچ یک از اسلات‌های مرکزی، جلو و عقب با دکمه‌ای پر نشده‌اند، دکمه‌های پیش‌فرض را برای این اسلات اضافه کنید.

شما می‌توانید از CommandButton.DisplayConstraints برای ایجاد پیش‌نمایشی از نحوه‌ی اعمال تنظیمات دکمه‌ی رسانه بسته به محدودیت‌های نمایش رابط کاربری استفاده کنید.

تنظیمات دکمه رسانه را تنظیم کنید

ساده‌ترین راه برای تنظیم تنظیمات دکمه رسانه، تعریف لیست هنگام ساخت MediaSession است. به عنوان یک جایگزین، می‌توانید MediaSession.Callback.onConnect را برای سفارشی‌سازی تنظیمات دکمه رسانه برای هر کنترلر متصل، بازنویسی کنید.

کاتلین

val mediaSession =
  MediaSession.Builder(context, player)
    .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
    .build()

جاوا

MediaSession mediaSession =
  new MediaSession.Builder(context, player)
      .setMediaButtonPreferences(ImmutableList.of(likeButton, favoriteButton))
      .build();

تنظیمات دکمه رسانه را پس از تعامل کاربر به‌روزرسانی کنید

پس از مدیریت یک تعامل با پخش‌کننده، ممکن است بخواهید دکمه‌های نمایش داده شده در رابط کاربری کنترلر را به‌روزرسانی کنید. یک مثال معمول، یک دکمه‌ی جابجایی است که پس از فعال شدن عملکرد مرتبط با این دکمه، آیکون و عملکرد آن تغییر می‌کند. برای به‌روزرسانی تنظیمات دکمه‌ی رسانه، می‌توانید از MediaSession.setMediaButtonPreferences برای به‌روزرسانی تنظیمات برای همه کنترلرها یا یک کنترلر خاص استفاده کنید:

کاتلین

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
  ImmutableList.of(likeButton, removeFromFavoritesButton))

جاوا

// Handle "favoritesButton" action, replace by opposite button
mediaSession.setMediaButtonPreferences(
    ImmutableList.of(likeButton, removeFromFavoritesButton));

دستورات سفارشی اضافه کنید و رفتار پیش‌فرض را سفارشی کنید

دستورات پخش‌کننده‌ی موجود را می‌توان با دستورات سفارشی گسترش داد و همچنین می‌توان دستورات پخش‌کننده‌ی ورودی و دکمه‌های رسانه را برای تغییر رفتار پیش‌فرض رهگیری کرد.

اعلان و مدیریت دستورات سفارشی

برنامه‌های رسانه‌ای می‌توانند دستورات سفارشی تعریف کنند که برای مثال می‌توانند در تنظیمات دکمه رسانه استفاده شوند. برای مثال، ممکن است بخواهید دکمه‌هایی پیاده‌سازی کنید که به کاربر اجازه دهد یک آیتم رسانه‌ای را در لیستی از آیتم‌های مورد علاقه ذخیره کند. MediaController دستورات سفارشی را ارسال می‌کند و MediaSession.Callback آنها را دریافت می‌کند.

برای تعریف دستورات سفارشی، باید MediaSession.Callback.onConnect() برای تنظیم دستورات سفارشی موجود برای هر کنترلر متصل، override کنید.

کاتلین

private class CustomMediaSessionCallback: MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  override fun onConnect(
    session: MediaSession,
    controller: MediaSession.ControllerInfo
  ): ConnectionResult {
    val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build()
  }
}

جاوا

class CustomMediaSessionCallback implements MediaSession.Callback {
  // Configure commands available to the controller in onConnect()
  @Override
  public ConnectionResult onConnect(
    MediaSession session,
    ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return new AcceptedResultBuilder(session)
        .setAvailableSessionCommands(sessionCommands)
        .build();
  }
}

برای دریافت درخواست‌های دستور سفارشی از یک MediaController ، متد onCustomCommand() را در Callback بازنویسی کنید.

کاتلین

private class CustomMediaSessionCallback: MediaSession.Callback {
  ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: MediaSession.ControllerInfo,
    customCommand: SessionCommand,
    args: Bundle
  ): ListenableFuture<SessionResult> {
    if (customCommand.customAction == SAVE_TO_FAVORITES) {
      // Do custom logic here
      saveToFavorites(session.player.currentMediaItem)
      return Futures.immediateFuture(
        SessionResult(SessionResult.RESULT_SUCCESS)
      )
    }
    ...
  }
}

جاوا

class CustomMediaSessionCallback implements MediaSession.Callback {
  ...
  @Override
  public ListenableFuture<SessionResult> onCustomCommand(
    MediaSession session, 
    ControllerInfo controller,
    SessionCommand customCommand,
    Bundle args
  ) {
    if(customCommand.customAction.equals(SAVE_TO_FAVORITES)) {
      // Do custom logic here
      saveToFavorites(session.getPlayer().getCurrentMediaItem());
      return Futures.immediateFuture(
        new SessionResult(SessionResult.RESULT_SUCCESS)
      );
    }
    ...
  }
}

شما می‌توانید با استفاده از ویژگی packageName از شیء MediaSession.ControllerInfo که به متدهای Callback ارسال می‌شود، پیگیری کنید که کدام کنترل‌کننده رسانه درخواستی را ارسال می‌کند. این به شما امکان می‌دهد رفتار برنامه خود را در پاسخ به یک دستور داده شده، چه از سیستم، چه از برنامه خودتان یا سایر برنامه‌های کلاینت، تنظیم کنید.

سفارشی‌سازی دستورات پیش‌فرض پخش‌کننده

تمام دستورات پیش‌فرض و مدیریت حالت‌ها به Player ای که در MediaSession قرار دارد، واگذار می‌شود. برای سفارشی‌سازی رفتار یک دستور تعریف‌شده در رابط Player ، مانند play() یا seekToNext() ، Player خود را قبل از ارسال به MediaSession ، در یک ForwardingSimpleBasePlayer قرار دهید:

کاتلین

val player = (logic to build a Player instance)

val forwardingPlayer = object : ForwardingSimpleBasePlayer(player) {
  // Customizations
}

val mediaSession = MediaSession.Builder(context, forwardingPlayer).build()

جاوا

ExoPlayer player = (logic to build a Player instance)

ForwardingSimpleBasePlayer forwardingPlayer =
    new ForwardingSimpleBasePlayer(player) {
      // Customizations
    };

MediaSession mediaSession =
  new MediaSession.Builder(context, forwardingPlayer).build();

برای اطلاعات بیشتر در مورد ForwardingSimpleBasePlayer ، به راهنمای ExoPlayer در مورد سفارشی‌سازی مراجعه کنید.

کنترل‌کننده‌ی درخواست‌کننده‌ی یک فرمان پخش‌کننده را شناسایی کنید

وقتی فراخوانی متد Player توسط MediaController انجام می‌شود، می‌توانید منبع مبدا را با MediaSession.controllerForCurrentRequest شناسایی کرده و ControllerInfo برای درخواست فعلی به دست آورید:

کاتلین

class CallerAwarePlayer(player: Player) :
  ForwardingSimpleBasePlayer(player) {

  override fun handleSeek(
    mediaItemIndex: Int,
    positionMs: Long,
    seekCommand: Int,
  ): ListenableFuture<*> {
    Log.d(
      "caller",
      "seek operation from package ${session.controllerForCurrentRequest?.packageName}",
    )
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand)
  }
}

جاوا

public class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  public CallerAwarePlayer(Player player) {
    super(player);
  }

  @Override
  protected ListenableFuture<?> handleSeek(
        int mediaItemIndex, long positionMs, int seekCommand) {
    Log.d(
        "caller",
        "seek operation from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    return super.handleSeek(mediaItemIndex, positionMs, seekCommand);
  }
}

سفارشی‌سازی مدیریت دکمه‌های رسانه

دکمه‌های رسانه‌ای، دکمه‌های سخت‌افزاری هستند که در دستگاه‌های اندروید و سایر دستگاه‌های جانبی مانند دکمه پخش/مکث روی هدست بلوتوث یافت می‌شوند. Media3 رویدادهای دکمه رسانه‌ای را هنگام ورود به جلسه برای شما مدیریت می‌کند و متد Player مناسب را در پخش‌کننده جلسه فراخوانی می‌کند.

توصیه می‌شود تمام رویدادهای دکمه رسانه ورودی در متد Player مربوطه مدیریت شوند. برای موارد استفاده پیشرفته‌تر، رویدادهای دکمه رسانه را می‌توان در MediaSession.Callback.onMediaButtonEvent(Intent) رهگیری کرد.

مدیریت خطا و گزارش آن

دو نوع خطا وجود دارد که یک جلسه منتشر می‌کند و به کنترل‌کننده‌ها گزارش می‌دهد. خطاهای مهلک، نقص فنی پخش پخش‌کننده جلسه را گزارش می‌دهند که پخش را قطع می‌کند. خطاهای مهلک هنگام وقوع به طور خودکار به کنترل‌کننده گزارش می‌شوند. خطاهای غیرمهلک، خطاهای غیرفنی یا مربوط به سیاست هستند که پخش را قطع نمی‌کنند و توسط برنامه به صورت دستی به کنترل‌کننده‌ها ارسال می‌شوند.

خطاهای پخش مهلک

یک خطای پخش مهلک توسط پخش‌کننده به جلسه گزارش می‌شود و سپس به کنترل‌کننده‌ها گزارش می‌شود تا از طریق Player.Listener.onPlayerError(PlaybackException) و Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) فراخوانی شوند.

در چنین حالتی، وضعیت پخش به STATE_IDLE منتقل می‌شود و MediaController.getPlaybackError() PlaybackException که باعث این انتقال شده است را برمی‌گرداند. یک کنترلر می‌تواند PlayerException.errorCode بررسی کند تا اطلاعاتی در مورد دلیل خطا به دست آورد.

تنظیم خطای پخش کننده سفارشی

علاوه بر خطاهای مهلک گزارش شده توسط پخش‌کننده، یک برنامه می‌تواند با استفاده از MediaSession.setPlaybackException(PlaybackException) یک PlaybackException سفارشی در سطح MediaSession تنظیم کند. این به برنامه اجازه می‌دهد تا وضعیت خطا را به کنترل‌کننده‌های متصل اعلام کند. این استثنا را می‌توان برای همه کنترل‌کننده‌های متصل یا برای یک ControllerInfo خاص تنظیم کرد.

وقتی یک برنامه با استفاده از این API یک PlaybackException تنظیم می‌کند:

  • به نمونه‌های متصل MediaController اطلاع داده خواهد شد. Listener.onPlayerError(PlaybackException) و Listener.onPlayerErrorChanged(@Nullable PlaybackException) روی کنترلر با استثنای ارائه شده فراخوانی می‌شوند.

  • متد MediaController.getPlayerError() PlaybackException تنظیم شده توسط برنامه را برمی‌گرداند.

  • وضعیت پخش برای کنترلرهای آسیب‌دیده به Player.STATE_IDLE تغییر خواهد کرد.

  • دستورات موجود حذف می‌شوند و فقط دستورات خواندنی مانند COMMAND_GET_TIMELINE در صورتی که قبلاً اعطا شده باشند، باقی می‌مانند. به عنوان مثال، وضعیت Timeline در حالتی که استثنا برای کنترلر اعمال شده است، ثابت می‌ماند. دستوراتی که سعی در تغییر وضعیت پخش‌کننده دارند، مانند COMMAND_PLAY ، تا زمانی که استثنای پخش برای کنترلر مورد نظر توسط برنامه حذف شود، حذف می‌شوند.

برای پاک کردن یک PlaybackException سفارشی که قبلاً تنظیم شده و بازیابی گزارش وضعیت پخش‌کننده به حالت عادی، یک برنامه می‌تواند MediaSession.setPlaybackException(/* playbackException= */ null) یا MediaSession.setPlaybackException(ControllerInfo, /* playbackException= */ null) را فراخوانی کند.

سفارشی‌سازی خطاهای مهلک

برای ارائه اطلاعات محلی و معنادار به کاربر، می‌توانید کد خطا، پیام خطا و موارد اضافی خطای پخش مهلک که از پخش‌کننده اصلی می‌آید را سفارشی کنید. این کار را می‌توان با استفاده از ForwardingPlayer هنگام ساخت جلسه انجام داد:

کاتلین

val forwardingPlayer = ErrorForwardingPlayer(player)
val session = MediaSession.Builder(context, forwardingPlayer).build()

جاوا

Player forwardingPlayer = new ErrorForwardingPlayer(player);
MediaSession session =
    new MediaSession.Builder(context, forwardingPlayer).build();

پخش‌کننده‌ی ارسال‌کننده می‌تواند از ForwardingSimpleBasePlayer برای رهگیری خطا و سفارشی‌سازی کد خطا، پیام یا موارد اضافی استفاده کند. به همین ترتیب، می‌توانید خطاهای جدیدی ایجاد کنید که در پخش‌کننده‌ی اصلی وجود ندارند:

کاتلین

class ErrorForwardingPlayer (private val context: Context, player: Player) :
    ForwardingSimpleBasePlayer(player) {

  override fun getState(): State {
    var state = super.getState()
    if (state.playerError != null) {
      state =
        state.buildUpon()
          .setPlayerError(customizePlaybackException(state.playerError!!))
          .build()
    }
    return state
  }

  fun customizePlaybackException(error: PlaybackException): PlaybackException {
    val buttonLabel: String
    val errorMessage: String
    when (error.errorCode) {
      PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
        buttonLabel = context.getString(R.string.err_button_label_restart_stream)
        errorMessage = context.getString(R.string.err_msg_behind_live_window)
      }
      else -> {
        buttonLabel = context.getString(R.string.err_button_label_ok)
        errorMessage = context.getString(R.string.err_message_default)
      }
    }
    val extras = Bundle()
    extras.putString("button_label", buttonLabel)
    return PlaybackException(errorMessage, error.cause, error.errorCode, extras)
  }
}

جاوا

class ErrorForwardingPlayer extends ForwardingSimpleBasePlayer {

  private final Context context;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
  }

  @Override
  protected State getState() {
    State state = super.getState();
    if (state.playerError != null) {
      state =
          state.buildUpon()
              .setPlayerError(customizePlaybackException(state.playerError))
              .build();
    }
    return state;
  }

  private PlaybackException customizePlaybackException(PlaybackException error) {
    String buttonLabel;
    String errorMessage;
    switch (error.errorCode) {
      case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
        buttonLabel = context.getString(R.string.err_button_label_restart_stream);
        errorMessage = context.getString(R.string.err_msg_behind_live_window);
        break;
      default:
        buttonLabel = context.getString(R.string.err_button_label_ok);
        errorMessage = context.getString(R.string.err_message_default);
        break;
    }
    Bundle extras = new Bundle();
    extras.putString("button_label", buttonLabel);
    return new PlaybackException(errorMessage, error.getCause(), error.errorCode, extras);
  }
}

خطاهای غیرمهلک

خطاهای غیرمهلک که از یک استثنای فنی ناشی نمی‌شوند ، می‌توانند توسط یک برنامه به همه یا به یک کنترل‌کننده خاص ارسال شوند:

کاتلین

val sessionError = SessionError(
  SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
  context.getString(R.string.error_message_authentication_expired),
)

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError)

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
mediaSession.mediaNotificationControllerInfo?.let {
  mediaSession.sendError(it, sessionError)
}

جاوا

SessionError sessionError = new SessionError(
    SessionError.ERROR_SESSION_AUTHENTICATION_EXPIRED,
    context.getString(R.string.error_message_authentication_expired));

// Option 1: Sending a nonfatal error to all controllers.
mediaSession.sendError(sessionError);

// Option 2: Sending a nonfatal error to the media notification controller only
// to set the error code and error message in the playback state of the platform
// media session.
ControllerInfo mediaNotificationControllerInfo =
    mediaSession.getMediaNotificationControllerInfo();
if (mediaNotificationControllerInfo != null) {
  mediaSession.sendError(mediaNotificationControllerInfo, sessionError);
}

وقتی یک خطای غیرمهلک به کنترل‌کننده اعلان رسانه ارسال می‌شود، کد خطا و پیام خطا در جلسه رسانه پلتفرم تکرار می‌شوند، در حالی که PlaybackState.state به STATE_ERROR تغییر نمی‌کند.

دریافت خطاهای غیرمهلک

یک MediaController با پیاده‌سازی MediaController.Listener.onError خطای غیرمهلک دریافت می‌کند:

کاتلین

val future = MediaController.Builder(context, sessionToken)
  .setListener(object : MediaController.Listener {
    override fun onError(controller: MediaController, sessionError: SessionError) {
      // Handle nonfatal error.
    }
  })
  .buildAsync()

جاوا

MediaController.Builder future =
    new MediaController.Builder(context, sessionToken)
        .setListener(
            new MediaController.Listener() {
              @Override
              public void onError(MediaController controller, SessionError sessionError) {
                // Handle nonfatal error.
              }
            });