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

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

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

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

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

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

यह कई मामलों में काम आता है. खास तौर पर, आपको 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 लाइब्रेरी, प्लेयर की स्थिति के हिसाब से मीडिया सेशन को अपने-आप अपडेट करती है. इसलिए, आपको प्लेयर से सेशन तक की मैपिंग को मैन्युअल तरीके से मैनेज करने की ज़रूरत नहीं है.

यह प्लैटफ़ॉर्म मीडिया सेशन से अलग है. इसमें आपको प्लेयर से अलग PlaybackState बनाना और बनाए रखना होता है. उदाहरण के लिए, किसी गड़बड़ी के बारे में बताने के लिए.

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

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

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

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

अन्य क्लाइंट को कंट्रोल करने की अनुमति देना

मीडिया सेशन, प्लेबैक को कंट्रोल करने का मुख्य तरीका है. इस कुकी की मदद से, बाहरी सोर्स से मिलने वाले निर्देशों को उस प्लेयर पर भेजा जा सकता है जो आपके मीडिया को चलाने का काम करता है. ये सोर्स, फ़िज़िकल बटन हो सकते हैं. जैसे, हेडसेट या टीवी के रिमोट कंट्रोल पर मौजूद 'चलाएं' बटन. इसके अलावा, ये सोर्स अप्रत्यक्ष निर्देश भी हो सकते हैं. जैसे, 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() को लागू करने का उदाहरण, सेशन डेमो ऐप्लिकेशन में देखा जा सकता है.

मीडिया बटन की सेटिंग मैनेज करना

हर कंट्रोलर, जैसे कि सिस्टम यूज़र इंटरफ़ेस (यूआई), Android Auto या Wear OS, यह तय कर सकता है कि उपयोगकर्ता को कौनसे बटन दिखाने हैं. आपको उपयोगकर्ता को कौनसे प्लेबैक कंट्रोल दिखाने हैं, यह बताने के लिए MediaSession पर मीडिया बटन की प्राथमिकताएं तय की जा सकती हैं. इन प्राथमिकताओं में, CommandButton इंस्टेंस की क्रम से लगाई गई सूची होती है. हर इंस्टेंस, उपयोगकर्ता इंटरफ़ेस में मौजूद बटन के लिए प्राथमिकता तय करता है.

कमांड बटन तय करना

CommandButton इंस्टेंस का इस्तेमाल, मीडिया बटन की प्राथमिकताओं को तय करने के लिए किया जाता है. हर बटन, यूज़र इंटरफ़ेस (यूआई) एलिमेंट के तीन पहलुओं के बारे में बताता है:

  1. आइकॉन, जो विज़ुअल लुक तय करता है. CommandButton.Builder बनाते समय, आइकॉन को पहले से तय किए गए किसी एक कॉन्स्टेंट पर सेट किया जाना चाहिए. ध्यान दें कि यह कोई असली बिटमैप या इमेज संसाधन नहीं है. सामान्य कॉन्स्टेंट की मदद से कंट्रोलर, अपने यूज़र इंटरफ़ेस (यूआई) में एक जैसा रंग-रूप और एहसास बनाए रखने के लिए सही संसाधन चुन सकते हैं. अगर पहले से तय किए गए आइकॉन कॉन्स्टेंट में से कोई भी आपके इस्तेमाल के उदाहरण के मुताबिक नहीं है, तो setCustomIconResId का इस्तेमाल किया जा सकता है.
  2. कमांड, यह तय करती है कि उपयोगकर्ता के बटन से इंटरैक्ट करने पर कौनसी कार्रवाई ट्रिगर होगी. Player.Command के लिए setPlayerCommand या पहले से तय किए गए या अपनी पसंद के मुताबिक SessionCommand के लिए setSessionCommand का इस्तेमाल किया जा सकता है.
  3. स्लॉट, यह तय करता है कि कंट्रोलर के यूज़र इंटरफ़ेस (यूआई) में बटन को कहां रखा जाना चाहिए. यह फ़ील्ड भरना ज़रूरी नहीं है. यह आइकॉन और कमांड के आधार पर अपने-आप सेट हो जाता है. उदाहरण के लिए, इससे यह तय किया जा सकता है कि किसी बटन को यूज़र इंटरफ़ेस (यूआई) के 'आगे बढ़ें' नेविगेशन एरिया में दिखाया जाना चाहिए, न कि डिफ़ॉल्ट 'ओवरफ़्लो' एरिया में.

Kotlin

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

Java

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 के लिए, बटन को उपलब्ध और अनुमति वाले पहले स्लॉट में रखें.
  2. अगर बीच के, आगे और पीछे के किसी भी स्लॉट में बटन नहीं जोड़ा गया है, तो इस स्लॉट के लिए डिफ़ॉल्ट बटन जोड़ें.

CommandButton.DisplayConstraints का इस्तेमाल करके, यह देखा जा सकता है कि यूज़र इंटरफ़ेस (यूआई) डिसप्ले की सीमाओं के आधार पर, मीडिया बटन की प्राथमिकताओं को कैसे हल किया जाएगा.

'मीडिया जोड़ें' बटन की प्राथमिकताएं सेट करना

मीडिया बटन की प्राथमिकताएं सेट करने का सबसे आसान तरीका यह है कि MediaSession बनाते समय सूची तय की जाए. इसके अलावा, कनेक्ट किए गए हर कंट्रोलर के लिए, मीडिया बटन की सेटिंग को पसंद के मुताबिक बनाने के लिए, MediaSession.Callback.onConnect को बदला जा सकता है.

Kotlin

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

Java

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

उपयोगकर्ता के इंटरैक्शन के बाद, मीडिया बटन की प्राथमिकताओं को अपडेट करना

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

Kotlin

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

Java

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

कस्टम कमांड जोड़ना और डिफ़ॉल्ट व्यवहार को पसंद के मुताबिक बनाना

प्लेयर के लिए उपलब्ध कमांड को कस्टम कमांड की मदद से बढ़ाया जा सकता है. साथ ही, डिफ़ॉल्ट व्यवहार को बदलने के लिए, प्लेयर के लिए आने वाली कमांड और मीडिया बटन को इंटरसेप्ट किया जा सकता है.

कस्टम कमांड के बारे में बताना और उन्हें मैनेज करना

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

कस्टम कमांड तय करने के लिए, आपको MediaSession.Callback.onConnect() को ओवरराइड करना होगा, ताकि हर कनेक्ट किए गए कंट्रोलर के लिए उपलब्ध कस्टम कमांड सेट की जा सकें.

Kotlin

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

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

म्यूज़िक प्लेयर के लिए डिफ़ॉल्ट तौर पर उपलब्ध निर्देशों को पसंद के मुताबिक बनाना

सभी डिफ़ॉल्ट कमांड और स्थिति को मैनेज करने का काम, Player को सौंपा जाता है. यह MediaSession पर होता है. Player इंटरफ़ेस में तय किए गए किसी निर्देश के व्यवहार को पसंद के मुताबिक बनाने के लिए, MediaSession को पास करने से पहले अपने Player को ForwardingSimpleBasePlayer में रैप करें. जैसे, play() या seekToNext():

Kotlin

val player = (logic to build a Player instance)

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

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

Java

ExoPlayer player = (logic to build a Player instance)

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

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

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

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

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

Kotlin

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

Java

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

मीडिया बटन के फ़ंक्शन को पसंद के मुताबिक बनाना

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

हमारा सुझाव है कि आने वाले सभी मीडिया बटन इवेंट को, इससे जुड़ी Player तरीके से हैंडल किया जाए. ज़्यादा बेहतर इस्तेमाल के लिए, मीडिया बटन के इवेंट को MediaSession.Callback.onMediaButtonEvent(Intent) में इंटरसेप्ट किया जा सकता है.

गड़बड़ी ठीक करना और रिपोर्ट करना

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

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

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

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

कस्टम प्लेयर की गड़बड़ी सेट करना

खिलाड़ी की ओर से रिपोर्ट की गई गंभीर गड़बड़ियों के अलावा, कोई ऐप्लिकेशन PlaybackException का इस्तेमाल करके MediaSession लेवल पर कस्टम PlaybackException सेट कर सकता है.MediaSession.setPlaybackException(PlaybackException) इससे ऐप्लिकेशन, कनेक्ट किए गए कंट्रोलर को गड़बड़ी की स्थिति के बारे में सूचना दे पाता है. अपवाद को कनेक्ट किए गए सभी कंट्रोलर या किसी खास ControllerInfo के लिए सेट किया जा सकता है.

जब कोई ऐप्लिकेशन इस एपीआई का इस्तेमाल करके 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 का इस्तेमाल करके, इसे हासिल किया जा सकता है:

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();

फ़ॉरवर्ड करने वाला प्लेयर, गड़बड़ी को रोकने के लिए ForwardingSimpleBasePlayer का इस्तेमाल कर सकता है. साथ ही, गड़बड़ी के कोड, मैसेज या अतिरिक्त जानकारी को अपनी पसंद के मुताबिक बना सकता है. इसी तरह, ऐसी नई गड़बड़ियां भी जनरेट की जा सकती हैं जो ओरिजनल प्लेयर में मौजूद नहीं हैं:

Kotlin

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

Java

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

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

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

Kotlin

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

Java

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 लागू करने पर, नुकसान न पहुंचाने वाली गड़बड़ी मिलती है:

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