Mediasession का इस्तेमाल करके, प्लेबैक को कंट्रोल करना और उसका विज्ञापन देना

मीडिया सेशन, ऑडियो या वीडियो प्लेयर के साथ इंटरैक्ट करने का एक यूनिवर्सल तरीका उपलब्ध कराते हैं. Media3 में, डिफ़ॉल्ट प्लेयर ExoPlayer क्लास है, जो Player इंटरफ़ेस को लागू करती है. मीडिया सेशन को प्लेयर से कनेक्ट करने से, ऐप्लिकेशन को बाहरी सोर्स से मीडिया प्लेबैक का विज्ञापन देने और निर्देश पाने की अनुमति मिल जाती है.

कमांड, फ़िज़िकल बटन से मिल सकते हैं. जैसे, हेडसेट या टीवी रिमोट कंट्रोल पर मौजूद प्ले बटन. ये निर्देश, मीडिया कंट्रोलर वाले क्लाइंट ऐप्लिकेशन से भी मिल सकते हैं. जैसे, Google Assistant को "रोकें" निर्देश देना. मीडिया सेशन, इन निर्देशों को मीडिया ऐप्लिकेशन के प्लेयर को सौंप देता है.

मीडिया सेशन कब चुनना चाहिए

MediaSession लागू करने पर, उपयोगकर्ताओं को प्लेबैक को कंट्रोल करने की अनुमति मिल जाती है:

  • हेडफ़ोन के ज़रिए. हेडफ़ोन पर अक्सर बटन या टच इंटरैक्शन होते हैं. इनकी मदद से, उपयोगकर्ता मीडिया को चला या रोक सकता है. साथ ही, अगले या पिछले ट्रैक पर जा सकता है.
  • Google Assistant से बात करके. डिवाइस पर चल रहे किसी भी मीडिया को रोकने के लिए, आम तौर पर "Ok Google, रोकें" कहा जाता है.
  • अपनी Wear OS स्मार्टवॉच के ज़रिए. इससे, फ़ोन पर वीडियो चलाते समय, वीडियो चलाने के सबसे सामान्य कंट्रोल को आसानी से ऐक्सेस किया जा सकता है.
  • मीडिया कंट्रोल की मदद से. इस कैरसेल में, चल रहे हर मीडिया सेशन के कंट्रोल दिखते हैं.
  • टीवी पर. प्लेबैक के लिए बने फ़िज़िकल बटन, प्लैटफ़ॉर्म पर प्लेबैक कंट्रोल, और पावर मैनेजमेंट की सुविधाओं का इस्तेमाल करने की अनुमति देता है. उदाहरण के लिए, अगर टीवी, साउंडबार या ए/वी रिसीवर बंद हो जाता है या इनपुट स्विच हो जाता है, तो ऐप्लिकेशन में प्लेबैक बंद हो जाना चाहिए.
  • साथ ही, ऐसी अन्य बाहरी प्रोसेस जिनका वीडियो चलाने पर असर पड़ता है.

यह कई कामों के लिए बेहतरीन है. खास तौर पर, आपको MediaSession का इस्तेमाल तब करना चाहिए, जब:

  • लंबी अवधि के वीडियो कॉन्टेंट स्ट्रीम किया जा रहा हो, जैसे कि फ़िल्में या लाइव टीवी.
  • लंबी अवधि के ऑडियो कॉन्टेंट स्ट्रीम किया जा रहा हो. जैसे, पॉडकास्ट या संगीत प्लेलिस्ट.
  • आपने टीवी ऐप्लिकेशन बनाया है.

हालांकि, इस्तेमाल के सभी उदाहरण MediaSession के साथ सही तरीके से काम नहीं करते. इन मामलों में, हो सकता है कि आप सिर्फ़ Player का इस्तेमाल करना चाहें:

  • आपने कम अवधि का कॉन्टेंट दिखाया है, जहां उपयोगकर्ता के जुड़ाव और इंटरैक्शन का महत्व होता है.
  • कोई भी वीडियो ऐक्टिव नहीं है. उदाहरण के लिए, उपयोगकर्ता किसी सूची को स्क्रोल कर रहा है और स्क्रीन पर एक साथ कई वीडियो दिख रहे हैं.
  • आपने सिर्फ़ एक बार दिखाए जाने वाले वीडियो को चलाया है. आपके हिसाब से, उपयोगकर्ता को यह वीडियो ज़रूर देखना चाहिए.
  • आपका कॉन्टेंट निजता के लिहाज़ से संवेदनशील है और आपको मीडिया मेटाडेटा को ऐक्सेस करने की अनुमति बाहरी प्रोसेस को नहीं देनी है. उदाहरण के लिए, ब्राउज़र में गुप्त मोड

अगर आपका इस्तेमाल का उदाहरण, ऊपर दिए गए किसी भी उदाहरण से मेल नहीं खाता है, तो इस बात पर विचार करें कि क्या आपको अपने ऐप्लिकेशन में वीडियो चलाना जारी रखना है, भले ही उपयोगकर्ता उसमें दिलचस्पी न दिखा रहा हो. अगर जवाब हां है, तो आपको MediaSession चुनना चाहिए. अगर जवाब 'नहीं' है, तो शायद आपको इसके बजाय Player का इस्तेमाल करना चाहिए.

मीडिया सेशन बनाना

मीडिया सेशन, उस प्लेयर के साथ रहता है जिसे वह मैनेज करता है. Context और Player ऑब्जेक्ट की मदद से, मीडिया सेशन बनाया जा सकता है. ज़रूरत पड़ने पर, आपको मीडिया सेशन बनाना और उसे शुरू करना चाहिए. जैसे, Activity या Fragment के onStart() या onResume() लाइफ़साइकल का तरीका या मीडिया सेशन और उससे जुड़े प्लेयर का मालिकाना हक रखने वाले Service का onCreate() तरीका.

मीडिया सेशन बनाने के लिए, Player को शुरू करें और इसे इस तरह MediaSession.Builder को दें:

Kotlin

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

Java

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

स्टेट को अपने-आप मैनेज करना

Media3 लाइब्रेरी, प्लेयर की स्थिति का इस्तेमाल करके मीडिया सेशन को अपने-आप अपडेट करती है. इसका मतलब है कि आपको प्लेयर से सेशन तक की मैपिंग को मैन्युअल तरीके से मैनेज करने की ज़रूरत नहीं है.

यह लेगसी तरीके से अलग है, जहां आपको प्लेयर से अलग PlaybackStateCompat बनाना और उसे मैनेज करना पड़ता था. उदाहरण के लिए, किसी भी गड़बड़ी का पता लगाने के लिए.

यूनीक सेशन आईडी

डिफ़ॉल्ट रूप से, MediaSession.Builder सेशन आईडी के तौर पर खाली स्ट्रिंग के साथ एक सेशन बनाता है. अगर किसी ऐप्लिकेशन को सिर्फ़ एक सेशन का इंस्टेंस बनाना है, तो यह तरीका काफ़ी होगा.

अगर किसी ऐप्लिकेशन को एक ही समय में कई सेशन इंस्टेंस मैनेज करने हैं, तो ऐप्लिकेशन को यह पक्का करना होगा कि हर सेशन का सेशन आईडी यूनीक हो. MediaSession.Builder.setId(String id) की मदद से सेशन बनाते समय, सेशन आईडी सेट किया जा सकता है.

अगर आपको गड़बड़ी का मैसेज IllegalStateException: Session ID must be unique. ID= के साथ IllegalStateException क्रैश होता हुआ दिखता है, तो हो सकता है कि इसी आईडी से बनाए गए पिछले इंस्टेंस के रिलीज़ होने से पहले, अचानक कोई सेशन शुरू हो गया हो. प्रोग्रामिंग की गड़बड़ी की वजह से सेशन लीक होने से रोकने के लिए, ऐसे मामलों का पता लगाया जाता है और अपवाद दिखाकर सूचना दी जाती है.

दूसरे क्लाइंट को कंट्रोल देना

प्लेबैक को कंट्रोल करने के लिए, मीडिया सेशन की ज़रूरत होती है. इसकी मदद से, बाहरी सोर्स से कमांड को उस प्लेयर पर भेजा जा सकता है जिस पर आपका मीडिया चलाया जाता है. ये बटन फ़िज़िकल बटन हो सकते हैं, जैसे कि हेडसेट या टीवी के रिमोट कंट्रोल पर मौजूद 'चलाएं' बटन. इसके अलावा, ये सीधे तौर पर न बताने वाले निर्देश भी हो सकते हैं, जैसे कि Google Assistant को "रोकें" निर्देश. इसी तरह, सूचनाएं पाने और लॉक स्क्रीन पर कंट्रोल करने की सुविधा के लिए, Android सिस्टम को ऐक्सेस दिया जा सकता है. इसके अलावा, Wear OS स्मार्टवॉच को ऐक्सेस दिया जा सकता है, ताकि स्मार्टवॉच की होम स्क्रीन से गाने चलाए जा सकें. बाहरी क्लाइंट, मीडिया कंट्रोलर का इस्तेमाल करके आपके मीडिया ऐप्लिकेशन को वीडियो चलाने के निर्देश दे सकते हैं. ये निर्देश आपके मीडिया सेशन को मिलते हैं, जो आखिर में मीडिया प्लेयर को निर्देश देते हैं.

MediaSession और MediaController के बीच इंटरैक्शन को दिखाने वाला डायग्राम.
पहली इमेज: मीडिया कंट्रोलर की मदद से, बाहरी सोर्स से मीडिया सेशन में कमांड भेजे जा सकते हैं.

जब कोई कंट्रोलर आपके मीडिया सेशन से कनेक्ट होने वाला होता है, तो onConnect() तरीका लागू होता है. दिए गए ControllerInfo का इस्तेमाल करके, यह तय किया जा सकता है कि अनुरोध को स्वीकार करना है या अस्वीकार करना है. उपलब्ध निर्देशों का एलान करें सेक्शन में, कनेक्शन के अनुरोध को स्वीकार करने का उदाहरण देखें.

कनेक्ट होने के बाद, कंट्रोलर से सेशन में वीडियो चलाने के निर्देश भेजे जा सकते हैं. इसके बाद, सेशन उन निर्देशों को प्लेयर को सौंप देता है. Player इंटरफ़ेस में बताए गए वीडियो चलाने और प्लेलिस्ट से जुड़े निर्देश, सेशन में अपने-आप लागू हो जाते हैं.

कॉलबैक के अन्य तरीकों की मदद से, पसंद के मुताबिक प्लेबैक निर्देश और प्लेलिस्ट में बदलाव करने के अनुरोधों को मैनेज किया जा सकता है. इन कॉलबैक में भी ControllerInfo ऑब्जेक्ट शामिल होता है, ताकि आप हर कंट्रोलर के हिसाब से, हर अनुरोध का जवाब देने के तरीके में बदलाव कर सकें.

प्लेलिस्ट में बदलाव करना

मीडिया सेशन, अपने प्लेयर की प्लेलिस्ट में सीधे बदलाव कर सकता है. इस बारे में ज़्यादा जानने के लिए, प्लेलिस्ट के लिए ExoPlayer की गाइड पढ़ें. अगर कंट्रोलर के पास COMMAND_SET_MEDIA_ITEM या COMMAND_CHANGE_MEDIA_ITEMS उपलब्ध है, तो वे भी प्लेलिस्ट में बदलाव कर सकते हैं.

प्लेलिस्ट में नए आइटम जोड़ते समय, आम तौर पर प्लेयर को MediaItem तय किए गए यूआरआई के साथ इंस्टेंस की ज़रूरत होती है, ताकि उन्हें चलाया जा सके. डिफ़ॉल्ट रूप से, नए जोड़े गए आइटम अपने-आप player.addMediaItem जैसे प्लेयर के तरीकों पर फ़ॉरवर्ड हो जाते हैं. ऐसा तब होता है, जब उनके लिए यूआरआई तय किया गया हो.

अगर आपको प्लेयर में जोड़े गए MediaItem इंस्टेंस को पसंद के मुताबिक बनाना है, तो onAddMediaItems() को बदला जा सकता है. यह चरण तब ज़रूरी होता है, जब आपको ऐसे कंट्रोलर के साथ काम करना हो जो तय किए गए यूआरआई के बिना मीडिया का अनुरोध करते हैं. इसके बजाय, आम तौर पर MediaItem में, अनुरोध किए गए मीडिया के बारे में बताने के लिए, इनमें से एक या उससे ज़्यादा फ़ील्ड सेट होते हैं:

  • MediaItem.id: एक सामान्य आईडी, जिससे मीडिया की पहचान की जाती है.
  • MediaItem.RequestMetadata.mediaUri: अनुरोध का यूआरआई, जो कस्टम स्कीमा का इस्तेमाल कर सकता है और ज़रूरी नहीं है कि प्लेयर उसे सीधे तौर पर चलाए.
  • MediaItem.RequestMetadata.searchQuery: टेक्स्ट वाली खोज क्वेरी, जैसे कि Google Assistant से की गई क्वेरी.
  • MediaItem.MediaMetadata: 'टाइटल' या 'कलाकार' जैसा स्ट्रक्चर्ड मेटाडेटा.

नई प्लेलिस्ट को अपनी पसंद के मुताबिक बनाने के ज़्यादा विकल्प देखने के लिए, onSetMediaItems() को भी बदला जा सकता है. इससे प्लेलिस्ट का शुरुआती हिस्सा और उसकी पोज़िशन तय की जा सकती है. उदाहरण के लिए, आपके पास अनुरोध किए गए किसी एक आइटम को पूरी प्लेलिस्ट में बड़ा करने का विकल्प होता है. साथ ही, प्लेयर को उस आइटम के इंडेक्स से चलाने का निर्देश भी दिया जा सकता है जिसका अनुरोध किया गया था. इस सुविधा के साथ onSetMediaItems() को लागू करने का सैंपल, सेशन डेमो ऐप्लिकेशन में देखा जा सकता है.

कस्टम लेआउट और कस्टम निर्देश मैनेज करना

नीचे दिए गए सेक्शन में क्लाइंट ऐप्लिकेशन के लिए कस्टम कमांड बटन के कस्टम लेआउट का विज्ञापन करने और नियंत्रकों को कस्टम कमांड भेजने की अनुमति देने का तरीका बताया गया है.

सेशन का कस्टम लेआउट तय करना

क्लाइंट ऐप्लिकेशन को यह बताने के लिए कि आपको उपयोगकर्ता को कौनसे वीडियो चलाने के कंट्रोल दिखाने हैं, अपनी सेवा के onCreate() तरीके में MediaSession बनाते समय, सेशन का कस्टम लेआउट सेट करें.

Kotlin

override fun onCreate() {
  super.onCreate()

  val likeButton = CommandButton.Builder()
    .setDisplayName("Like")
    .setIconResId(R.drawable.like_icon)
    .setSessionCommand(SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING))
    .build()
  val favoriteButton = CommandButton.Builder()
    .setDisplayName("Save to favorites")
    .setIconResId(R.drawable.favorite_icon)
    .setSessionCommand(SessionCommand(SAVE_TO_FAVORITES, Bundle()))
    .build()

  session =
    MediaSession.Builder(this, player)
      .setCallback(CustomMediaSessionCallback())
      .setCustomLayout(ImmutableList.of(likeButton, favoriteButton))
      .build()
}

Java

@Override
public void onCreate() {
  super.onCreate();

  CommandButton likeButton = new CommandButton.Builder()
    .setDisplayName("Like")
    .setIconResId(R.drawable.like_icon)
    .setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING))
    .build();
  CommandButton favoriteButton = new CommandButton.Builder()
    .setDisplayName("Save to favorites")
    .setIconResId(R.drawable.favorite_icon)
    .setSessionCommand(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
    .build();

  Player player = new ExoPlayer.Builder(this).build();
  mediaSession =
      new MediaSession.Builder(this, player)
          .setCallback(new CustomMediaSessionCallback())
          .setCustomLayout(ImmutableList.of(likeButton, favoriteButton))
          .build();
}

उपलब्ध प्लेयर और पसंद के मुताबिक निर्देशों की जानकारी देना

मीडिया ऐप्लिकेशन, कस्टम निर्देश तय कर सकते हैं. उदाहरण के लिए, इनका इस्तेमाल कस्टम लेआउट में किया जा सकता है. उदाहरण के लिए, हो सकता है कि आप ऐसे बटन लागू करना चाहें जिनकी मदद से उपयोगकर्ता, मीडिया आइटम को पसंदीदा आइटम की सूची में सेव कर सके. MediaController, पसंद के मुताबिक निर्देश भेजता है और MediaSession.Callback उन्हें स्वीकार करता है.

आपके पास यह तय करने का विकल्प होता है कि आपके मीडिया सेशन से कनेक्ट होने पर, MediaController के लिए कौनसे कस्टम सेशन निर्देश उपलब्ध हों. ऐसा करने के लिए, MediaSession.Callback.onConnect() को बदलें. onConnect कॉलबैक मैथड में, MediaController से कनेक्शन का अनुरोध स्वीकार करते समय, उपलब्ध निर्देशों के सेट को कॉन्फ़िगर करें और वापस करें:

Kotlin

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

Java

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 से कस्टम कमांड के अनुरोध पाने के लिए, Callback में onCustomCommand() तरीके को बदलें.

Kotlin

private inner 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)
      )
    }
    ...
  }
}

Java

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)
      );
    }
    ...
  }
}

यह ट्रैक किया जा सकता है कि कौनसा मीडिया कंट्रोलर, MediaSession.ControllerInfo ऑब्जेक्ट की packageName प्रॉपर्टी का इस्तेमाल करके अनुरोध कर रहा है. यह प्रॉपर्टी, Callback तरीकों में पास की गई है. इससे, किसी दिए गए निर्देश के जवाब में अपने ऐप्लिकेशन के व्यवहार को ज़रूरत के मुताबिक बनाया जा सकता है. ऐसा तब किया जा सकता है, जब निर्देश सिस्टम, आपके ऐप्लिकेशन या दूसरे क्लाइंट ऐप्लिकेशन से मिले हो.

उपयोगकर्ता के इंटरैक्शन के बाद कस्टम लेआउट अपडेट करना

कस्टम निर्देश या अपने प्लेयर के साथ किसी अन्य इंटरैक्शन को मैनेज करने के बाद, हो सकता है कि आप कंट्रोलर के यूज़र इंटरफ़ेस (यूआई) में दिखाया गया लेआउट अपडेट करना चाहें. इसका एक सामान्य उदाहरण, टॉगल बटन है. इस बटन से जुड़ी कार्रवाई ट्रिगर करने के बाद, इसका आइकॉन बदल जाता है. लेआउट अपडेट करने के लिए, MediaSession.setCustomLayout का इस्तेमाल करें:

Kotlin

val removeFromFavoritesButton = CommandButton.Builder()
  .setDisplayName("Remove from favorites")
  .setIconResId(R.drawable.favorite_remove_icon)
  .setSessionCommand(SessionCommand(REMOVE_FROM_FAVORITES, Bundle()))
  .build()
mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton))

Java

CommandButton removeFromFavoritesButton = new CommandButton.Builder()
  .setDisplayName("Remove from favorites")
  .setIconResId(R.drawable.favorite_remove_icon)
  .setSessionCommand(new SessionCommand(REMOVE_FROM_FAVORITES, new Bundle()))
  .build();
mediaSession.setCustomLayout(ImmutableList.of(likeButton, removeFromFavoritesButton));

प्लेबैक कमांड के व्यवहार को पसंद के मुताबिक बनाएं

Player इंटरफ़ेस में बताए गए किसी निर्देश के व्यवहार को पसंद के मुताबिक बनाने के लिए, जैसे कि play() या seekToNext(), अपने Player को ForwardingPlayer में रैप करें.

Kotlin

val player = ExoPlayer.Builder(context).build()

val forwardingPlayer = object : ForwardingPlayer(player) {
  override fun play() {
    // Add custom logic
    super.play()
  }

  override fun setPlayWhenReady(playWhenReady: Boolean) {
    // Add custom logic
    super.setPlayWhenReady(playWhenReady)
  }
}

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

Java

ExoPlayer player = new ExoPlayer.Builder(context).build();

ForwardingPlayer forwardingPlayer = new ForwardingPlayer(player) {
  @Override
  public void play() {
    // Add custom logic
    super.play();
  }

  @Override
  public void setPlayWhenReady(boolean playWhenReady) {
    // Add custom logic
    super.setPlayWhenReady(playWhenReady);
  }
};

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

ForwardingPlayer के बारे में ज़्यादा जानकारी के लिए, कस्टमाइज़ेशन के बारे में ExoPlayer गाइड देखें.

प्लेयर कमांड का अनुरोध करने वाले कंट्रोलर की पहचान करना

जब Player तरीके को कॉल करने के लिए MediaController का इस्तेमाल किया जाता है, तो MediaSession.controllerForCurrentRequest की मदद से कॉल के सोर्स की पहचान की जा सकती है. साथ ही, मौजूदा अनुरोध के लिए ControllerInfo हासिल किया जा सकता है:

Kotlin

class CallerAwareForwardingPlayer(player: Player) :
  ForwardingPlayer(player) {

  override fun seekToNext() {
    Log.d(
      "caller",
      "seekToNext called from package ${session.controllerForCurrentRequest?.packageName}"
    )
    super.seekToNext()
  }
}

Java

public class CallerAwareForwardingPlayer extends ForwardingPlayer {
  public CallerAwareForwardingPlayer(Player player) {
    super(player);
  }

  @Override
  public void seekToNext() {
    Log.d(
        "caller",
        "seekToNext called from package: "
            + session.getControllerForCurrentRequest().getPackageName());
    super.seekToNext();
  }
}

मीडिया बटन के निर्देशों का पालन करना

मीडिया बटन, Android डिवाइसों और अन्य डिवाइसों पर मौजूद हार्डवेयर बटन होते हैं. जैसे, ब्लूटूथ हेडसेट पर मौजूद चलाएं/रोकें बटन. जब लोग सेशन पर आते हैं, तब Media3 आपके लिए मीडिया बटन के इवेंट मैनेज करता है. साथ ही, सेशन प्लेयर पर सही Player तरीके का इस्तेमाल करता है.

कोई ऐप्लिकेशन, MediaSession.Callback.onMediaButtonEvent(Intent) को बदलकर डिफ़ॉल्ट तरीके को बदल सकता है. ऐसे मामले में, ऐप्लिकेशन के पास एपीआई से जुड़ी सभी खास बातों को खुद मैनेज करने का विकल्प होता है.

गड़बड़ी को मैनेज करना और उसकी शिकायत करना

सेशन दो तरह की गड़बड़ियां दिखाता है और उन्हें कंट्रोलर को रिपोर्ट करता है. गंभीर गड़बड़ियां, सेशन प्लेयर के प्लेबैक में होने वाली तकनीकी गड़बड़ी की जानकारी देती हैं. इससे प्लेबैक में रुकावट आती है. गंभीर गड़बड़ियां होने पर, उन्हें कंट्रोलर को अपने-आप सूचना दी जाती है. गैर-घातक गड़बड़ियां, ऐसी गड़बड़ियां होती हैं जो तकनीकी या नीति से जुड़ी नहीं होतीं. ये गड़बड़ियां, वीडियो चलाने में रुकावट नहीं डालतीं. साथ ही, इन्हें ऐप्लिकेशन मैन्युअल तरीके से कंट्रोल करने वालों को भेजता है.

वीडियो चलाने में हुई गंभीर गड़बड़ियां

प्लेबैक से जुड़ी गंभीर गड़बड़ी की शिकायत, प्लेयर सेशन में की जाती है. इसके बाद, Player.Listener.onPlayerError(PlaybackException) और Player.Listener.onPlayerErrorChanged(@Nullable PlaybackException) के ज़रिए कॉल करने के लिए, कंट्रोल करने वालों से शिकायत की जाती है.

ऐसे मामले में, वीडियो चलाने की स्थिति को STATE_IDLE पर ट्रांसफ़र कर दिया जाता है और MediaController.getPlaybackError() उस PlaybackException को दिखाता है जिसकी वजह से ट्रांज़िशन हुआ. गड़बड़ी की वजह के बारे में जानकारी पाने के लिए, कंट्रोलर PlayerException.errorCode की जांच कर सकता है.

इंटरऑपरेबिलिटी के लिए, प्लैटफ़ॉर्म सेशन के PlaybackStateCompat में एक गंभीर गड़बड़ी की कॉपी बनाई जाती है. इसके लिए, इसकी स्थिति को STATE_ERROR में बदलकर, PlaybackException के हिसाब से गड़बड़ी कोड और मैसेज सेट किया जाता है.

गंभीर गड़बड़ी को पसंद के मुताबिक बनाना

उपयोगकर्ता को स्थानीय भाषा में सही और काम की जानकारी देने के लिए, सेशन बनाते समय ForwardingPlayer का इस्तेमाल करके, गड़बड़ी के कोड, गड़बड़ी के मैसेज, और कोई गंभीर गड़बड़ी के मैसेज को अपनी पसंद के मुताबिक बनाया जा सकता है:

Kotlin

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

Java

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

फ़ॉरवर्ड करने वाला प्लेयर, असल प्लेयर में Player.Listener रजिस्टर करता है. साथ ही, उन कॉलबैक को इंटरसेप्ट करता है जो गड़बड़ी की रिपोर्ट करते हैं. इसके बाद, पसंद के मुताबिक बनाया गया PlaybackException, उन दर्शकों को भेजा जाता है जो फ़ॉरवर्ड करने वाले प्लेयर पर रजिस्टर हैं. इसके काम करने के लिए, फ़ॉरवर्ड करने वाला प्लेयर, Player.addListener और Player.removeListener को बदल देता है, ताकि वह उन दर्शकों का ऐक्सेस पा सके जिनके लिए गड़बड़ी का कोड, मैसेज या अन्य जानकारी भेजनी है:

Kotlin

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

  private val listeners: MutableList<Player.Listener> = mutableListOf()

  private var customizedPlaybackException: PlaybackException? = null

  init {
    player.addListener(ErrorCustomizationListener())
  }

  override fun addListener(listener: Player.Listener) {
    listeners.add(listener)
  }

  override fun removeListener(listener: Player.Listener) {
    listeners.remove(listener)
  }

  override fun getPlayerError(): PlaybackException? {
    return customizedPlaybackException
  }

  private inner class ErrorCustomizationListener : Player.Listener {

    override fun onPlayerErrorChanged(error: PlaybackException?) {
      customizedPlaybackException = error?.let { customizePlaybackException(it) }
      listeners.forEach { it.onPlayerErrorChanged(customizedPlaybackException) }
    }

    override fun onPlayerError(error: PlaybackException) {
      listeners.forEach { it.onPlayerError(customizedPlaybackException!!) }
    }

    private 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)
        }
        // Apps can customize further error messages by adding more branches.
        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)
    }

    override fun onEvents(player: Player, events: Player.Events) {
      listeners.forEach {
        it.onEvents(player, events)
      }
    }
    // Delegate all other callbacks to all listeners without changing arguments like onEvents.
  }
}

Java

private static class ErrorForwardingPlayer extends ForwardingPlayer {

  private final Context context;
  private List<Player.Listener> listeners;
  @Nullable private PlaybackException customizedPlaybackException;

  public ErrorForwardingPlayer(Context context, Player player) {
    super(player);
    this.context = context;
    listeners = new ArrayList<>();
    player.addListener(new ErrorCustomizationListener());
  }

  @Override
  public void addListener(Player.Listener listener) {
    listeners.add(listener);
  }

  @Override
  public void removeListener(Player.Listener listener) {
    listeners.remove(listener);
  }

  @Nullable
  @Override
  public PlaybackException getPlayerError() {
    return customizedPlaybackException;
  }

  private class ErrorCustomizationListener implements Listener {

    @Override
    public void onPlayerErrorChanged(@Nullable PlaybackException error) {
      customizedPlaybackException =
          error != null ? customizePlaybackException(error, context) : null;
      for (int i = 0; i < listeners.size(); i++) {
        listeners.get(i).onPlayerErrorChanged(customizedPlaybackException);
      }
    }

    @Override
    public void onPlayerError(PlaybackException error) {
      for (int i = 0; i < listeners.size(); i++) {
        listeners.get(i).onPlayerError(checkNotNull(customizedPlaybackException));
      }
    }

    private PlaybackException customizePlaybackException(
        PlaybackException error, Context context) {
      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;
        // Apps can customize further error messages by adding more case statements.
        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);
    }

    @Override
    public void onEvents(Player player, Events events) {
      for (int i = 0; i < listeners.size(); i++) {
        listeners.get(i).onEvents(player, events);
      }
    }
    // Delegate all other callbacks to all listeners without changing arguments like onEvents.
  }
}

नुकसान न पहुंचाने वाली गड़बड़ियां

ऐसी गड़बड़ियां जो तकनीकी गड़बड़ी की वजह से नहीं होती हैं, उन्हें ऐप्लिकेशन से सभी या किसी खास कंट्रोलर पर भेजा जा सकता है:

Kotlin

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

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

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

Java

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

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

// Interoperability: Sending a nonfatal error to the media notification controller 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);
}

मीडिया सूचना कंट्रोलर को भेजी गई सामान्य गड़बड़ी, प्लैटफ़ॉर्म सेशन के PlaybackStateCompat में कॉपी की जाती है. इसलिए, सिर्फ़ गड़बड़ी का कोड और गड़बड़ी का मैसेज PlaybackStateCompat पर सेट किया जाता है, जबकि PlaybackStateCompat.state को STATE_ERROR में नहीं बदला जाता.

साधारण गड़बड़ियां पाएं

MediaController को MediaController.Listener.onError लागू करने पर, गड़बड़ी का मैसेज मिलता है, लेकिन प्रोसेस पूरी हो जाती है:

Kotlin

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

Java

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