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)
    .setPlayerCommand(Player.COMMAND_SEEK_FORWARD)
    .setSlots(CommandButton.SLOT_FORWARD)
    .build()

Java

CommandButton button =
    new CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD_15)
        .setPlayerCommand(Player.COMMAND_SEEK_FORWARD)
        .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 onConnectAsync(
    session: MediaSession,
    controller: ControllerInfo,
  ): ListenableFuture<ConnectionResult> {
    val sessionCommands =
      ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
        .add(SessionCommand(SAVE_TO_FAVORITES, Bundle.EMPTY))
        .build()
    return Futures.immediateFuture(
      AcceptedResultBuilder(session, controller)
        .setAvailableSessionCommands(sessionCommands)
        .build()
    )
  }
}

Java

private static class CustomMediaSessionCallback implements MediaSession.Callback {

  // Configure commands available to the controller in onConnect()
  @Override
  public ListenableFuture<ConnectionResult> onConnectAsync(
      MediaSession session, ControllerInfo controller) {
    SessionCommands sessionCommands =
        ConnectionResult.DEFAULT_SESSION_COMMANDS
            .buildUpon()
            .add(new SessionCommand(SAVE_TO_FAVORITES, new Bundle()))
            .build();
    return Futures.immediateFuture(
        new AcceptedResultBuilder(session, controller)
            .setAvailableSessionCommands(sessionCommands)
            .build());
  }
}

MediaController से कस्टम कमांड के अनुरोध पाने के लिए, Callback में onCustomCommand() तरीके को बदलें.

Kotlin

private class CustomCallback : MediaSession.Callback {
  // ...
  override fun onCustomCommand(
    session: MediaSession,
    controller: 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))
    }
    // ...
    return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
  }
}

Java

private static class CustomCallback 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));
    }
    // ...
    return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS));
  }
}

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

डिफ़ॉल्ट प्लेयर के लिए, पसंद के मुताबिक निर्देश सेट करना

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

Kotlin

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

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

Java

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

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

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

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

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

Kotlin

private class CallerAwarePlayer(player: Player) : ForwardingSimpleBasePlayer(player) {
  private lateinit var session: MediaSession

  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

private static final class CallerAwarePlayer extends ForwardingSimpleBasePlayer {
  private MediaSession session;

  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 की जांच करके गड़बड़ी की वजह के बारे में जानकारी पा सकता है.

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

प्लेयर से रिपोर्ट की गई गंभीर गड़बड़ियों के अलावा, कोई ऐप्लिकेशन MediaSession.setPlaybackException(PlaybackException) का इस्तेमाल करके, MediaSession लेवल पर कस्टम 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 session = MediaSession.Builder(context, ErrorForwardingPlayer(context, player)).build()

Java

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

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

Kotlin

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

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

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