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

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

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

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

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

  • हेडफ़ोन से. आम तौर पर, हेडफ़ोन पर बटन या टच इंटरैक्शन होते हैं. इनका इस्तेमाल करके, उपयोगकर्ता मीडिया चला सकता है या रोक सकता है. इसके अलावा, अगले या पिछले ट्रैक पर जा सकता है.
  • Gemini या 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= दिखता है, तो ऐसा हो सकता है कि उसी आईडी के साथ पहले से बनाए गए इंस्टेंस के रिलीज़ होने से पहले, अचानक कोई सेशन बन गया हो. प्रोग्रामिंग की गड़बड़ी की वजह से सेशन लीक न हों, इसके लिए ऐसे मामलों का पता लगाया जाता है और अपवाद थ्रो करके सूचना दी जाती है.

अन्य क्लाइंट को कंट्रोल देना

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

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

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

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

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

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

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

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

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

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

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

प्लेलिस्ट का मेटाडेटा पब्लिश करना

Media3, एपीआई उपलब्ध कराता है. इनकी मदद से, ट्रैक प्लेबैक के साथ-साथ प्लेलिस्ट का स्ट्रक्चर्ड मेटाडेटा पब्लिश किया जा सकता है. मीडिया सेशन के दौरान, प्लेलिस्ट में शामिल आइटम और जानकारी देने वाले मेटाडेटा को पॉप्युलेट करने के तरीके को स्टैंडर्ड बनाने से, Android को आपके ऐप्लिकेशन में मीडिया चलाने के लिए सिस्टम-लेवल के एंट्री पॉइंट दिखाने में मदद मिलती है.

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

प्लेलिस्ट आईडी और प्लेलिस्ट का मेटाडेटा तय करना

Media3 में, प्लेलिस्ट की पहचान और प्लेलिस्ट के बारे में जानकारी देने वाले मेटाडेटा को दो MediaMetadata स्कोप में बांटा गया है:

  • आइटम-लेवल प्लेलिस्ट आईडी (MediaItem.mediaMetadata.playlistId):
    • MediaMetadata.Builder.setPlaylistId को आइटम के MediaMetadata पर कॉल करके, चलाए जा रहे MediaItem को प्लेलिस्ट के हिस्से के तौर पर मार्क करें. वहीं, MediaItem.mediaId अलग-अलग ट्रैक की खास तौर पर पहचान करता है.
    • playlistId, ऐप्लिकेशन से तय की गई एक ऐसी स्ट्रिंग है जो साफ़ तौर पर नहीं दिखती. यह स्ट्रिंग, सभी सेशन में प्लेलिस्ट की खास तौर पर पहचान करती है. ऐसा तब तक होता है, जब तक प्लेलिस्ट को फिर से शुरू किया जा सकता है. उदाहरण के लिए, 37i9dQZF1DX76Wlfdnj7AP. साथ ही, इसका इस्तेमाल किसी दूसरी प्लेलिस्ट के लिए नहीं किया जाना चाहिए. कनेक्ट किए गए कंट्रोलर इस आईडी को नहीं समझते. वे इसे सेव करते हैं और प्लेलिस्ट को फिर से शुरू करने के लिए, आपके ऐप्लिकेशन पर वापस भेजते हैं.
    • प्लेलिस्ट में मौजूद हर ट्रैक में एक ही playlistId होता है. playlistId को सेट न करें (null). ऐसा उन ट्रैक के लिए करें जो प्लेलिस्ट के कॉन्टेक्स्ट से नहीं चलाए जाते. जैसे, कोई स्टैंडअलोन ट्रैक.
  • प्लेयर-लेवल प्लेलिस्ट मेटाडेटा (Player.getPlaylistMetadata):
    • प्लेलिस्ट के ब्यौरे वाले एट्रिब्यूट (setTitle, setArtworkUri, और setMediaType) के साथ, MediaMetadata के खास इंस्टेंस को पॉप्युलेट करें. साथ ही, Player.setPlaylistMetadata का इस्तेमाल करके, इसे प्लेयर पर सेट करें.
    • जब कनेक्ट किया गया कोई कंट्रोलर, नॉन-नुल currentMediaItem.mediaMetadata.playlistId का पता लगाता है, तो वह प्लेलिस्ट का टाइटल और आर्टवर्क यूआरआई पाने के लिए, MediaController.getPlaylistMetadata की जांच करता है.
    • जब भी चालू प्लेलिस्ट में बदलाव हो, तो प्लेलिस्ट का मेटाडेटा अपडेट करें. उदाहरण के लिए, जब उपयोगकर्ता कोई दूसरी प्लेलिस्ट शुरू करता है, तो getPlaylistMetadata हमेशा उस प्लेलिस्ट के बारे में बताता है जिसे मौजूदा आइटम के playlistId से रेफ़र किया गया है.

यहां दिए गए उदाहरण में, Player पर प्लेलिस्ट का मेटाडेटा पब्लिश करने और उस प्लेलिस्ट में ट्रैक MediaItem को जोड़ने का तरीका बताया गया है:

Kotlin

// 1. Declare the playlist descriptive metadata on the Player
val playlistMetadata =
  MediaMetadata.Builder()
    .setTitle("Morning Vibes")
    .setArtworkUri(Uri.parse("https://example.com/image/morning-vibes"))
    .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
    .build()
player.setPlaylistMetadata(playlistMetadata)

// 2. Mark the playing track(s) with the playlistId it belongs to
val trackItem =
  MediaItem.Builder()
    .setMediaId("foobar:track:9b4d7g1j3l6n8p2s5t0v4x")
    .setMediaMetadata(
      MediaMetadata.Builder()
        .setTitle("Example Track")
        .setArtist("Example Artist")
        .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
        .setPlaylistId("a8f3k9m2p7q4r1w6x5z0v2")
        .build()
    )
    .build()
player.setMediaItem(trackItem)

Java

// 1. Declare the playlist descriptive metadata on the Player
MediaMetadata playlistMetadata =
    new MediaMetadata.Builder()
        .setTitle("Morning Vibes")
        .setArtworkUri(Uri.parse("https://example.com/image/morning-vibes"))
        .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
        .build();
player.setPlaylistMetadata(playlistMetadata);

// 2. Mark the playing track(s) with the playlistId it belongs to
MediaItem trackItem =
    new MediaItem.Builder()
        .setMediaId("foobar:track:9b4d7g1j3l6n8p2s5t0v4x")
        .setMediaMetadata(
            new MediaMetadata.Builder()
                .setTitle("Example Track")
                .setArtist("Example Artist")
                .setMediaType(MediaMetadata.MEDIA_TYPE_MUSIC)
                .setPlaylistId("a8f3k9m2p7q4r1w6x5z0v2")
                .build())
        .build();
player.setMediaItem(trackItem);

प्लेलिस्ट फिर से चलाना

जब कोई उपयोगकर्ता फिर से शुरू करने के लिए कोई प्लेलिस्ट चुनता है, तो कंट्रोलर आपके ऐप्लिकेशन के मीडिया सेशन से कनेक्ट हो जाता है. साथ ही, वह एक प्लेलिस्ट MediaItem भेजता है, जिसका mediaId, सेव किए गए playlistId पर सेट होता है:

Kotlin

val playlistItem =
  MediaItem.Builder()
    .setMediaId("a8f3k9m2p7q4r1w6x5z0v2")
    .setMediaMetadata(
      MediaMetadata.Builder()
        .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
        .setIsPlayable(true)
        .build()
    )
    .build()
controller.setMediaItem(playlistItem)
controller.prepare()
controller.play()

Java

MediaItem playlistItem =
    new MediaItem.Builder()
        .setMediaId("a8f3k9m2p7q4r1w6x5z0v2")
        .setMediaMetadata(
            new MediaMetadata.Builder()
                .setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
                .setIsPlayable(true)
                .build())
        .build();
controller.setMediaItem(playlistItem);
controller.prepare();
controller.play();

आपका ऐप्लिकेशन, प्लेलिस्ट MediaItem को MediaSession.Callback.onSetMediaItems (या onAddMediaItems) में मौजूद, चलाए जा सकने वाले ट्रैक में बड़ा करता है. साथ ही, पिछले सेक्शन में दिखाए गए तरीके से, playlistId और playlistMetadata को फिर से पब्लिश करता है.

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

हर कंट्रोलर, जैसे कि सिस्टम यूज़र इंटरफ़ेस (यूआई), 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.onConnectAsync() को बदला जा सकता है.

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.onConnectAsync() को बदलना होगा. इससे, कनेक्ट किए गए हर कंट्रोलर के लिए उपलब्ध कस्टम कमांड सेट किए जा सकेंगे.

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 इंटरफ़ेस में तय किए गए किसी निर्देश के व्यवहार को पसंद के मुताबिक बनाने के लिए, अपने Player को ForwardingSimpleBasePlayer में रैप करें. इसके बाद, उसे MediaSession को पास करें. जैसे, play() या seekToNext():

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 की गाइड देखें.

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

जब किसी Player तरीके को कॉल करने की शुरुआत MediaController से होती है, तो 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() में इंटरसेप्ट किया जा सकता है.

गड़बड़ी को ठीक करना और उसकी शिकायत करना

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

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

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

ऐसे मामले में, वीडियो चलाने की स्थिति STATE_IDLE में बदल जाती है और MediaController.getPlayerError(), ट्रांज़िशन की वजह बनने वाले PlaybackException को दिखाता है. कंट्रोलर, PlaybackException.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) को कॉल कर सकता है.

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

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

Kotlin

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

Java

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

गड़बड़ी को रोकने और गड़बड़ी के कोड, मैसेज या अन्य चीज़ों को पसंद के मुताबिक बनाने के लिए, आपकी ForwardingSimpleBasePlayer सबक्लास getState() को बदल सकती है. इसी तरह, नई गड़बड़ियां भी जनरेट की जा सकती हैं, जो ओरिजनल प्लेयर में मौजूद नहीं हैं:

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